Two merge-blocking bugs in the shared in-process run driver, both rooted in `readResult` scanning the whole child session and deriving the stop reason only from `turn/end`: - A pre-turn `cancel()` cleared the queued prompt before any `turn/end` was logged, so the run settled `error` instead of `aborted`, violating the `SubagentRun.cancel()` contract. The driver now tracks that a cancel was requested and maps the no-turn case to `aborted`. - A fork child whose own turn produced no `assistant/message` returned the SEEDED parent's last message as a `completed` success. `readResult` now scopes to the child's OWN events (after the seed prefix), so a message-less child yields empty output. Both fixes carry a regression test proven to go red on the pre-fix driver. Also: correct the `SubagentRun.id` / event-payload docs (it is the child AGENT id, not a session id — the backend mints distinct tokens); refresh the stale `coding-agent` welcome string (subagent is now a tool); and replace the stale `TODO(sub-agents)` "deferred" prose in the Agent interface, core.md, and architecture.md with an accurate pointer to the realized seam.
core/ — product API spine
The packages every harness build is assembled from: the session log, the system-prompt assembly, the tool registry, the agent vocabulary, and the one concrete loop that drives them. These are product packages — the stable surface plugins and consumers build against.
| Package | Role | ctx key |
|---|---|---|
session/ |
Event-sourced session log + in-memory store | ctx.sessions |
system-prompt/ |
Prompt-section + tool-schema assembly registry | ctx.systemPrompt |
tools/ |
Tool registry + tools/execute waterfall |
ctx.tools |
agent/ |
Agent interface, registry, agent/* event vocabulary |
ctx.agents |
agent-loop/ |
The concrete loop plugin: ReactLoopAgent + the loop driver |
ctx.agentLoop |
agent-core/ |
Bundle plugin: the providerless/executor-less/UI-less spine as code | (loads the spine) |
agent-loop is the one concrete implementation of the agent seam and lives here because it is the harness's default product loop; everything else in core/ is interface/vocabulary. Plugins depend on the agent vocabulary, never on agent-loop directly, so the loop stays swappable.
agent-core is the composition counterpart: one bundle plugin that loads the whole providerless spine (timer + llm + sessions + system-prompt + tools + agents + invariants + tool-bash + agent-loop) and forwards agent-loop's agents list as its own config. App packages (ui/stdio-agent, ui/acp-agent) consume it and add only a front door; a leaf adds only the swappable backends. It lives in core/ because it composes exclusively core/ + interface packages and ships no provider, executor, or UI of its own.