Skip to content

Refactor: split the AgentDO god-object (workers/api/src/agent-do.ts, 1298 LOC) #136

Closed

Description

@serge-ivo

Follow-up to the largest-files audit (#125). workers/api/src/agent-do.ts (1298 LOC): DO chat loop, tool execution, storage wiring, repo ingest, and the alarm state machine in one class.

Task (behavior-preserving)

  • Extract repo ingest + alarm state machine (fetching → indexing → summarizing → done) to its own module.
  • Move remaining inline chat/tool logic into agent-think.ts.
  • Keep the DO a thin orchestrator.

Constraints

  • Keep all api tests green; no change to the internal https://agent/* route contract.

Activity

  1. serge-ivo commented on Aug 6, 2026

    @serge-ivo
    ContributorAuthor

    Recheck 2026-08-06 — the file has grown since this was filed.

    Ticket says 1298 LOC. Actual today: 1383 (+85). The god-object is getting bigger while the split ticket waits, so the estimate in the title understates the work and will keep doing so.

    The extraction this issue names — repo ingest + the fetching → indexing → summarizing → done alarm state machine — is still the right first cut, and is the part least entangled with the chat loop.

    Worth noting the same pattern on its sibling #138 (1127 → 1341, +214). Both are losing ground; if neither is going to be picked up soon, a cheap guard is a size check in CI that fails on growth, so at least the number stops moving in the wrong direction.

  2. serge-ivo commented on Aug 6, 2026

    @serge-ivo
    ContributorAuthor

    Shipped in c63e823 — but measuring the file first changed two of the three tasks, so here is what was actually done and why.

    What the file contains. 1499 LOC, not 1298. Composition: routing table 160 · chat/turn lifecycle 293 · knowledge base 232 · collections+records 93 · files 98 · state 103 · websocket 67 · messages 49 · tasks 51 · memory 44 · repo-ingest wrappers 64 · search/activity/summaries/context 67.

    Task 1 (extract repo ingest + the fetching → indexing → summarizing → done alarm machine) was already done — lib/repo-ingest-runner.ts, 294 LOC with its own tests and injected fetchers. Only 64 lines of thin wrappers remain in the DO. Nothing to do.

    Task 2 (move remaining inline chat/tool logic into agent-think.ts) is also already done — think() is a 20-line delegation to runAgentThink, and tool execution lives in agent-think.ts / agent-do-tools.ts. What is left around it is not logic but the turn lifecycle (#251): sequencing message writes, broadcasts, status transitions and the in-flight marker over liveTurns / summarizing — state only this DO instance can vouch for. Extracting it would require handing those refs straight back out, which hides the coupling rather than removing it (same conclusion as #138). Declined; the diff does not enter that region at all.

    What did have a seam — 490 lines needing no DO state whatsoever:

    • agent-do-storage-routes.ts — collections, records, files, vector search, activity, summaries, user context. Every one was getState() → getStorageEngine() → engine.x() → json(). They now take the narrowest slice of the engine they use (Pick<…>), so a test hands them a two-line fake instead of a Durable Object. withEngine in the DO resolves the engine and answers the same 404, before the body is read, exactly as each did inline.
    • agent-do-knowledge.ts — the kb: documents and their vectors, behind the injected-storage interface repo-ingest-runner established. This is where the subtle rules live and they now have tests: a doc is saved even when it cannot be indexed but reports vectorized:false (PLATFORM_AI_ENABLED off = silent-success trap (ingest reports OK with dead RAG) #22); a delete removes the VECTORS FIRST and refuses with 503 if that fails, keeping the document so the user can retry ([bug] A failed vector delete is swallowed and unrecoverable — deleted knowledge keeps answering RAG queries #242); Import URL shares the same 20-document ceiling.

    Deliberately not extracted: the routing table (one place to read the https://agent/* contract — a matcher plus a dispatch switch would split it across two files for no testable decision); memory / tasks / messages CRUD (mem: / task: / msg: key formats are read by the chat loop, so a module would hand its state back); the chat turn lifecycle, above.

    Verification. The removed text was diffed against the new modules with only the intended substitutions normalized. Storage routes differ by exactly one line (state.model → the model parameter); knowledge differs by exactly one (getAllKnowledge now takes its store as an argument), at identical statement counts.

    agent-do.ts 1499 → 1039. No existing test changed. 3253 → 3292 tests, 229 → 231 files, all green; typecheck and biome clean. Route contract unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions