feat(agents): DefaultAgentDispatch orchestrator wiring all four ports #518
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks#518
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
User story
As an upstream caller (flow-runner, webhook handler, foreman), I want a concrete
AgentDispatchPortimplementation that wiresClaudeAgentPort,ContainerLifecyclePort,McpRegistryPort, and a worker registry, so that dispatch is a single call.Status — restart after closed PR #531 (2026-04-28)
First attempt (
boss/518→ PR #531) was written in parallel with #514–#517 / #527 / #529 / #533, against earlier port shapes that did not survive review. PR closed, branch retained for reference. This issue restarts the work against main's canonical port shapes.Canonical ports (read these first)
All three ports already exist on
main. Use these shapes verbatim — do not redefine.apps/server/src/domain/ports/agent-dispatch-port.ts—AgentDispatchRequest { agentType, repo: AgentDispatchRepo, issueOrPr, prompt, forgeToken?, resumeSessionKey?, metadata };AgentDispatchHandle { taskId, worker, containerId?, sessionKey };AgentDispatchStatusis flat (state: "queued" | "running" | "succeeded" | "failed" | "cancelled", ISO timestamps,prUrl?);steer()andkill()returnvoid.apps/server/src/domain/ports/container-lifecycle-port.ts—ensureReady → ContainerHandle { mode, container, hostCwd, inContainerCwd, execPath };buildExecArgs(handle, env);release(handle, reason).apps/server/src/domain/ports/mcp-registry-port.ts—serversFor(agent, RepoBinding)andallowedTools(agent, RepoBinding). NodefaultTokenaccessor — adapters resolve the token from the agent internally.Main today has a 31-line thin-wrapper
apps/server/src/infrastructure/agents/default-agent-dispatch.tsthat delegates torunAgentTask. This issue replaces that wrapper with the full orchestrator.Acceptance criteria
Implementation
apps/server/src/infrastructure/agents/default-agent-dispatch.ts— full orchestrator replaces the thin wrapperdispatch()orchestrates: pre-flight container viaContainerLifecyclePort.ensureReady→ resolve MCP servers viaMcpRegistryPort.serversFor→ enqueue runner closure onWorkerRegistryPort→ run viaClaudeAgentPort→ persist task event streamforgeTokenoverride beats the per-agent default for both MCP servers and the env bindingsteer()proxies toClaudeAgentPortwrite-side handle keyed bytaskIdkill()releases container, marks task killed, closes worker slot. Idempotent on already-settled tasksstatus()lifts queued/running from a live table and settled outcomes from a bounded recordapps/server/src/domain/ports/worker-registry-port.ts— port + in-memory FIFO adapter. Salvage verbatim from closedboss/518branch (9aaed6c) — reviewer flagged it as uncontroversialDefensive
Map<string, SettledTask>backingstatus()must have an LRU cap (~1000 entries) — main's M25 service is long-running and cannot leak completed-task records until the DB lift landsinfrastructure/*.McpServerSpecand friends live indomain/ports/or@claude-hooks/sharedEquivalence
runAgentTask()— same FIFO ordering, same(forge, agentType, repo, issueOrPr)session key, first-event session capture, container release in the runner'sfinallyadapter-factory.tsProxy)Tests
forgeTokenoverride → MCP spec carries override, forge adapter constructed with overridekill()mid-task → container released, task marked killed, secondkill()is no-opsteer()mid-task → message reaches the Claude write-side handle; post-settlesteer()returns gracefully (does not throw)Out of scope
runAgentTask()callers — already done in main via #533/task/:id/*endpointsstatus()(separate follow-up after this lands)References
boss/518, kept for reference)apps/server/src/infrastructure/agents/default-agent-dispatch.ts(introduced by #533,8cd16f0)apps/server/src/domain/ports/