Multi-turn conversation with subagent #500
Replies: 2 comments
|
Possibly related contribution from @timonkrebs — opened #505 which appears to be addressing this. Their summary: Adds a new Implementation: timonkrebs/flue@main (diff) This comment was posted automatically when #505 was redirected. The implementation is preserved on the branch above so it can inform the work here. |
|
The lifecycle inversion makes sense. One contract I would make explicit before merging is what invalidates the handle. There are three different endings that should not collapse into close():
Even with durable subagent recovery intentionally out of scope, the API should expose a terminal reason and reject future prompt calls deterministically. Otherwise callers cannot distinguish completed cleanup from a child that vanished halfway through a tool operation. I would also define parent settle separately from parent abort: if the parent’s current prompt completes while a spawned child remains open, either the child is explicitly allowed to outlive that operation or settle must wait for all owned children. Both are valid; leaving it implicit creates leaks and nondeterministic shutdown. In Better Agent we supervise native Claude, Codex, and Gemini children with persistent parent-owned records, and the difficult cases have consistently been ownership, terminal-state projection, and restart reconciliation rather than the prompt API itself. I maintain it; the project is source-available and free for non-commercial use, while commercial use requires separate permission: https://github.com/ofekron/better-agent AI-assistance disclosure: this comment was drafted by Codex under the maintainer’s authorization and reviewed in Better Agent. |
Uh oh!
There was an error while loading. Please reload this page.
RFC
Summary
Add
session.spawn()— a way to open a multi-turn conversation with a subagent and drive it across several prompts — alongside the existing one-shotsession.task()delegation.Background & Motivation
This comes out of building a real project on Flue — timonkrebs/CheQ-Mate — which needs to hold a multi-turn conversation with a subagent: ask a specialist something, read the answer, then follow up while it keeps its accumulated context.
Today the only way to reach a subagent is
session.task(text, { agent }). Internally that creates a child session, sends it exactly one prompt, and disposes it — the child is a fullSession(it hasprompt/skill/task/shell/fsand retains its own history), but its lifecycle is bounded to a single prompt.pi-agent-coresupports multi-turn subagent work, but Flue currently collapses it into fire-and-forget delegation: there is no public handle to keep talking to a child once it has answered. The docs make the limitation explicit — subagents are reachable only through a single delegatedtask()call.This blocks a common pattern: application/workflow code that wants a back-and-forth with a specialist — ask, read the answer, then follow up while the child keeps its accumulated context — without re-sending the whole history on every call. Users have to fake it by stuffing prior turns into a fresh
task()prompt each time, which loses the child's own conversation state and re-pays context cost.The gap is shallow: the machinery for a subagent conversation already exists in the child
Session. The only reason it's one-shot is thattask()'s implementation closes the child immediately after the single prompt.Goals
task()child — not a new separately addressable agent endpoint, and no new HTTP/persistence surface.task()semantics and every existing caller stay unchanged.task()everywhere except lifecycle.Non-goals: making subagents addressable over HTTP; changing durable subagent recovery (a spawned child, like a programmatic
task(), carries no parenttasktool call and would live only for the process).Example
Sketch of the surface:
Semantically,
spawn()istask()with the lifecycle inverted: create the child, register it so parent abort/settle cascades, then leave it open until the handle closes — rather than create → one prompt → close. Unliketask(), it doesn't hold the parent's exclusive operation lock, so the handle outlives any single operation and eachhandle.prompt()runs as an operation on the child.Reference implementation (for illustration only, not a PR request): a working version —
session.spawn()+SubagentHandle, docs, CHANGELOG, and tests (context retention, parent isolation, close idempotency,await usingdisposal, undeclared-subagent rejection) — is attimonkrebs/fluecommit941441d(git difftouchespackages/runtime/src/{session,types,index}.tsandpackages/sdk/src/types.ts). Happy to reshape it however you'd guide the design.All reactions