mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-27 04:30:59 +00:00
feat: report RFD delegations as linked session messages (#1405)
## Summary For a client on the subagents RFD (every ACP v2 client, and v1 clients that declare `subagents`), session messages now replace the Agent/Task call that launches a child. The parent no longer sees that tool call. AIR's form is unchanged. - **Prompt.** The launch's prompt is a `session_message` in both transcripts: the parent's outgoing view and the child's incoming one. Both use the launch's tool use id, and each names the other with `recipientMessageId` / `senderMessageId` from agentclientprotocol/agent-client-protocol#2465. - **Answer.** A foreground child's answer, from the launch's result, is the child's `session_message` to the parent (`<launch id>:reply`). It uses the result as `AgentReporter` renders it, so the model-facing `agentId: …` line and `<usage>` block are gone. A failure or cancellation comes from the child's `idle` stop reason, which it already reported. A background launch's result gets no reply. - **Hand-back mode.** Claude Code (seen with 2.1.303) can have the child hand its report back with a `SubagentHandback` call; the Agent result then only says that the report "was delivered to you as a message". That call is reported as the child's message to the parent, in both transcripts and linked the same way, and the launch's reply is skipped for that run. A failed hand-back delivered nothing, so it stays a tool call of the child. - **Unexposed work.** A launch whose child is never exposed is shown as a tool call after all, with its final status (`unexposedControlFallback`, the former `failedControlFallback`). - **Hidden launches by id.** The adapter's own report that closes an unfinished call (for example at a forced cancel) carries no `toolName`, so the runtime tracks hidden launches by id to keep that report from reaching the client for a call it never saw. - **Replay** sends the same messages from the child's transcript: the launch's prompt, the reply, and hand-backs. A launch without Claude Code's metadata file keeps its tool call and the missing-history notice. Replay can't tell a child's runs apart, so any hand-back in its transcript suppresses its launch's reply. - **SendMessage** calls stay tool calls of their sender, and the child gets the incoming view without references, as before. - README's subagent section describes the new behavior. Do not merge before agentclientprotocol/agent-client-protocol#2465 settles the field names: a merge to `main` publishes a preview right away. Clients on the published schema ignore the two fields (no `deny_unknown_fields` in the Rust schema; the scenario validation with the SDK's zod schema passes). ## Testing - `npm run build`, `npm run check` and `npm run test:run` pass (57 files, 4 skipped as before). - The six v2 `subagent-*.jsonl` scenario snapshots changed: the launch's tool-call updates are gone, the parent gets the outgoing view, the child's view carries `senderMessageId`, and foreground results became `:reply` messages. AIR snapshots are unchanged. - New unit tests cover hidden launches, nested launches, replies, failed and background launches, unexposed launches with both final statuses, the close report without a tool name, and hand-back success and failure, live and on replay. - A real run: the built adapter driven over v2 stdio with Claude Code, one foreground subagent asked to answer PONG, then `session/resume` with replay in a fresh process. Live and replay both showed the linked prompt (parent → child), the linked hand-back "PONG" (child → parent), and no Agent tool call.
This commit is contained in:
19
README.md
19
README.md
@@ -47,9 +47,18 @@ Task subagent as its own child session, following the [subagents RFD](https://gi
|
||||
`end_turn` or `cancelled` when the SDK gives the reason. A failure is `idle` with the `error`
|
||||
stop reason and the SDK's error text as its `error`. The child keeps its session for every
|
||||
delegation to it.
|
||||
- The prompt of the Agent or Task call that launches the child, and the message of each
|
||||
SendMessage call that resumes it, is a `session_message` in the child's transcript. The tool
|
||||
call stays a tool call of the session that made it.
|
||||
- Session messages replace the Agent or Task call that launches the child, which its parent is
|
||||
not shown:
|
||||
- The launch's prompt is a `session_message` in both transcripts: the parent's outgoing view and
|
||||
the child's incoming one. Both use the launch's tool use id, and each names the other with the
|
||||
draft RFD's `recipientMessageId` and `senderMessageId`.
|
||||
- A foreground child's answer, from the launch's result, is the child's `session_message` to
|
||||
the parent. In Claude Code's hand-back mode, the child's `SubagentHandback` call is its
|
||||
message to the parent instead, in both transcripts and linked the same way.
|
||||
- A launch whose child is never exposed is shown as a tool call after all, with its final
|
||||
status.
|
||||
- The message of a SendMessage call that resumes the child is a `session_message` in the child's
|
||||
transcript. The SendMessage call stays a tool call of the session that made it.
|
||||
- `session/cancel` on the parent interrupts Claude Code, which stops the foreground children that
|
||||
the turn waits on. Background children keep running, as the RFD prefers; a client stops one with
|
||||
its own `cancel`. Each stopped child reports `idle` with `cancelled` from Claude Code's own
|
||||
@@ -65,8 +74,8 @@ Task subagent as its own child session, following the [subagents RFD](https://gi
|
||||
exits is `idle` with no stop reason.
|
||||
- A child whose parent is not yet known when it ends is not exposed, since the RFD forbids
|
||||
guessing its parent.
|
||||
- A replay restores each child under its live session id, from its own transcript: its prompt and
|
||||
each SendMessage message are `session_message`s, and its end is an `idle` before the response.
|
||||
- A replay restores each child under its live session id, from its own transcript, with the same
|
||||
session messages as live, and its end is an `idle` before the response.
|
||||
A launch without Claude Code's metadata file for the child stays an ordinary tool call of the
|
||||
parent, and a notice, for a client that takes notices, says that subagent history is missing.
|
||||
- After a load or resume, with or without replay, a SendMessage that resumes an agent from before
|
||||
|
||||
Reference in New Issue
Block a user