feat(x): spawn-agent — run sub-agents as headless child turns

Adds the agent-as-tool capability deferred in turn-runtime-design §29.2:
a `spawn-agent` builtin that runs a sub-agent in its own standalone
headless turn and returns its final answer to the parent. Multiple
spawn calls in one assistant batch run concurrently (previous commit).

- RequestedAgent is now a union: by-id (unchanged shape) | inline
  {name, instructions, model?, tools?}. Inline definitions persist
  verbatim in turn_created and resolve to the same immutable snapshot.
- Agent resolution splits by variant: DispatchingAgentResolver narrows
  the union once; InlineAgentResolver materializes inline specs
  (builtin catalog validation, headless default profile when tools are
  omitted); RealAgentResolver keeps the by-id path byte-identical. The
  builtin→ToolDescriptor conversion is extracted to a shared helper.
- The spawn handler (RealToolRegistry branch → runSpawnedAgent) runs
  the child via HeadlessAgentRunner on the parent's model by default,
  clamps the model-call budget at 20, cascades the parent's abort
  signal, and records {kind:"subagent", childTurnId} as durable tool
  progress — the only parent→child link; no parentTurnId is added to
  the schema. Task-level failures return as conversational isError
  results, never terminal.
- Depth is capped at 1: both resolvers strip spawn-agent from children
  (inline always; by-id via the new subagent composition flag) and the
  handler refuses child-shaped parents outright.
- Renderer: spawn-agent calls render as a SubAgentBlock — a collapsed
  status card that expands to the child's live transcript
  (CompactConversation over sessions:getTurn, polled at 1s while
  running; standalone child turns don't reach the session bus).
- The BuiltinTools entry gives copilot (and other catalog-attached
  agents) the tool automatically; its execute is the degraded legacy
  path only, since the turn runtime intercepts builtin:spawn-agent.

Schema note: RequestedAgent widened under schemaVersion 1 (pre-release)
— requires wiping ~/.rowboat/storage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Ramnique Singh 2026-07-08 23:31:41 +05:30
parent d2b68a4684
commit b62ed6a2f4
21 changed files with 1145 additions and 49 deletions

View file

@ -1895,6 +1895,17 @@ tool handler may create and execute child turns, forward progress, and return a
parent tool result. No subflow or parent-turn concept is added to this initial
turn schema.
Implemented (v1) as the `spawn-agent` builtin: a sync tool whose handler
(`RealToolRegistry`) runs the child as a standalone headless turn
(`runSpawnedAgent`), records `{kind: "subagent", childTurnId}` as durable
tool progress (the only parent→child link), and returns the child's final
text plus a status envelope. `RequestedAgent` is a union of by-id and inline
variants; inline agents resolve through `InlineAgentResolver`. Depth is
capped at 1: both resolvers strip the spawn tool from children, and the
handler refuses child-shaped parents. Parallel fan-out comes from concurrent
sync-tool execution (§10.5), not from async suspension; async (restart
survivability for long children) remains future work.
### 29.3 Reliability enhancements
- External-input idempotency.