diff --git a/agent-client-protocol-schema/src/v1/client.rs b/agent-client-protocol-schema/src/v1/client.rs index ba2233a49..69997dbd6 100644 --- a/agent-client-protocol-schema/src/v1/client.rs +++ b/agent-client-protocol-schema/src/v1/client.rs @@ -1616,7 +1616,10 @@ impl RequiresActionStateUpdate { /// snapshot normally. /// /// This is not a task outcome or session closure. It does not cancel work, resolve -/// pending requests, or revoke capabilities. Capabilities are updated separately. +/// pending requests, or revoke capabilities. If a control is no longer available, +/// the Agent updates the capabilities explicitly. Unfinished tool calls and +/// terminals stay as last reported. The Agent does not invent a final status +/// for them, and Clients do not present them as confirmed live activity. #[cfg(feature = "unstable_subagents")] #[serde_as] #[skip_serializing_none] diff --git a/agent-client-protocol-schema/src/v2/client.rs b/agent-client-protocol-schema/src/v2/client.rs index b7d76b491..5ecfcbc22 100644 --- a/agent-client-protocol-schema/src/v2/client.rs +++ b/agent-client-protocol-schema/src/v2/client.rs @@ -1928,7 +1928,10 @@ impl RequiresActionStateUpdate { /// snapshot normally. /// /// This is not a task outcome or session closure. It does not cancel work, resolve -/// pending requests, or revoke capabilities. Capabilities are updated separately. +/// pending requests, or revoke capabilities. If a control is no longer available, +/// the Agent updates the capabilities explicitly. Unfinished tool calls and +/// terminals stay as last reported. The Agent does not invent a final status +/// for them, and Clients do not present them as confirmed live activity. #[cfg(feature = "unstable_subagents")] #[serde_as] #[skip_serializing_none] diff --git a/docs/protocol/v1/draft/prompt-turn.mdx b/docs/protocol/v1/draft/prompt-turn.mdx index a877d671f..7145ae11f 100644 --- a/docs/protocol/v1/draft/prompt-turn.mdx +++ b/docs/protocol/v1/draft/prompt-turn.mdx @@ -110,6 +110,11 @@ set to the parent, and **SHOULD NOT** report it as a `user_message_chunk`. Clients can then render it as a message from the parent session rather than from the user. See [Delegated tasks](/rfds/subagents#delegated-tasks). +When the Agent has no evidence of who sent a message the child received, it +omits `senderSessionId` and **MUST NOT** assume the parent. It links two views +only when it can establish that they are the same message, and leaves them +unlinked otherwise. + ## The prompt turn lifecycle
diff --git a/docs/protocol/v1/draft/schema.mdx b/docs/protocol/v1/draft/schema.mdx index 8285c6f3f..7d59a275b 100644 --- a/docs/protocol/v1/draft/schema.mdx +++ b/docs/protocol/v1/draft/schema.mdx @@ -9644,7 +9644,10 @@ current activity, but may retain it as last known. A later state replaces this snapshot normally. This is not a task outcome or session closure. It does not cancel work, resolve -pending requests, or revoke capabilities. Capabilities are updated separately. +pending requests, or revoke capabilities. If a control is no longer available, +the Agent updates the capabilities explicitly. Unfinished tool calls and +terminals stay as last reported. The Agent does not invent a final status +for them, and Clients do not present them as confirmed live activity. **Type:** Object diff --git a/docs/protocol/v1/draft/tool-calls.mdx b/docs/protocol/v1/draft/tool-calls.mdx index a7da6262c..810180739 100644 --- a/docs/protocol/v1/draft/tool-calls.mdx +++ b/docs/protocol/v1/draft/tool-calls.mdx @@ -359,6 +359,11 @@ session's work state. Agents send it only when the Client advertises the `subage sends a child is reported as a [session-directed message](/protocol/v1/draft/prompt-turn#session-directed-messages-unstable), not through a tool call. +`content` on `tool_call_update` replaces the whole array, so an Agent that +sends new content includes the session references again. When a tool addresses +a session by a name or another handle, the Agent adds a reference only if it +can resolve the handle to a known session. + ## Following the Agent Tool calls can report the file locations they're working with. Clients can use this to follow along, showing which files the Agent is reading or modifying as it works. diff --git a/docs/protocol/v2/draft/prompt-lifecycle.mdx b/docs/protocol/v2/draft/prompt-lifecycle.mdx index 3762b68fd..334d62e32 100644 --- a/docs/protocol/v2/draft/prompt-lifecycle.mdx +++ b/docs/protocol/v2/draft/prompt-lifecycle.mdx @@ -141,6 +141,11 @@ set to the parent, and **SHOULD NOT** report it as a `user_message`. Clients can then render it as a message from the parent session rather than from the user. See [Delegated tasks](/rfds/subagents#delegated-tasks). +When the Agent has no evidence of who sent a message the child received, it +omits `senderSessionId` and **MUST NOT** assume the parent. It links two views +only when it can establish that they are the same message, and leaves them +unlinked otherwise. + ## Foreground and background work **Foreground work** is the Agent's active response or workflow in the session, including model processing, tool execution, and waiting for user action needed to continue that work. The Agent **MUST** treat that work as foreground whether it was started by a user prompt or by the Agent, for example to pursue an ongoing goal, continue earlier work, or act on a completed background task. diff --git a/docs/protocol/v2/draft/schema.mdx b/docs/protocol/v2/draft/schema.mdx index abfbae772..51f739557 100644 --- a/docs/protocol/v2/draft/schema.mdx +++ b/docs/protocol/v2/draft/schema.mdx @@ -11525,7 +11525,10 @@ current activity, but may retain it as last known. A later state replaces this snapshot normally. This is not a task outcome or session closure. It does not cancel work, resolve -pending requests, or revoke capabilities. Capabilities are updated separately. +pending requests, or revoke capabilities. If a control is no longer available, +the Agent updates the capabilities explicitly. Unfinished tool calls and +terminals stay as last reported. The Agent does not invent a final status +for them, and Clients do not present them as confirmed live activity. **Type:** Object diff --git a/docs/protocol/v2/draft/tool-calls.mdx b/docs/protocol/v2/draft/tool-calls.mdx index 7a340f22a..b9aaba341 100644 --- a/docs/protocol/v2/draft/tool-calls.mdx +++ b/docs/protocol/v2/draft/tool-calls.mdx @@ -750,6 +750,11 @@ session's work state. The task a parent sends a child is reported as a [session-directed message](/protocol/v2/draft/prompt-lifecycle#session-directed-messages-unstable), not through a tool call. +`content` on `tool_call_update` replaces the whole array, so an Agent that +sends new content includes the session references again. When a tool addresses +a session by a name or another handle, the Agent adds a reference only if it +can resolve the handle to a known session. + ## Following the Agent Tool calls can report the file locations they're working with. Clients can use this to follow along, showing which files the Agent is reading or modifying as it works. diff --git a/docs/rfds/subagents.mdx b/docs/rfds/subagents.mdx index 076042caf..f2803013c 100644 --- a/docs/rfds/subagents.mdx +++ b/docs/rfds/subagents.mdx @@ -474,6 +474,14 @@ Client that receives conflicting references treats the views as unlinked. A reference does not indicate that the recipient finished processing the message, and it does not establish ownership. +An Agent links two views only when it can establish that they are the same +message. A provider-supplied identifier shared by both sides is the strongest +evidence. Without one, the Agent chooses how to match them, for example by +recipient and content. Equal content alone can match the wrong entry when a +sender repeats a message, and delivery may transform the content. When the +Agent cannot establish the match, it leaves the views unlinked. Unlinked views +are valid, and Clients display each one in its own transcript. + Known participant information must remain consistent with the enclosing transcript and earlier updates for that message. When both IDs are known, they identify different sessions and the outer `params.sessionId` matches one @@ -574,7 +582,10 @@ form acknowledges recipient processing or establishes new ownership. Actual delivery is handled by the Agent. The Agent reports each side only when it can observe that side: an outgoing -entry is not sufficient evidence to manufacture an incoming entry. Clients +entry is not sufficient evidence to manufacture an incoming entry. The Agent +may report an outgoing view when it observes the send start or once the send +completes. It **SHOULD NOT** report an outgoing view for a send that it knows +failed before the message left the sender. Clients **MUST NOT** infer receipt or synthesize an Agent-reported incoming transcript entry solely from an outgoing entry. This does not prevent a clearly labeled projection of outgoing messages in a cross-session view. The two views have @@ -641,6 +652,14 @@ ownership, grant Client-initiated controls, or report that the referenced session received or finished any work. Its tool call's status does not define the referenced session's lifetime or work state. +`content` on a tool call update replaces the whole array. An Agent that sends +new content for a tool call includes its session references again, or the +update removes them. An update that omits `content` leaves them in place. + +A tool may address a session by a name or another provider handle instead of +its session ID. The Agent adds a reference only when it can resolve that +handle to a known session. It **MUST NOT** guess the session. + In v1, `session` content is covered by the `subagents` Client capability like the rest of this RFD. In v2, receivers that do not recognize it preserve it as unknown tool call content. @@ -656,6 +675,13 @@ parent's conversation also records the delegation, Agents **SHOULD** report the outgoing view in the parent's transcript and link the two views. Later instructions to the same child use the same form. +Some providers record input in the child's conversation without recording who +sent it, and a sibling or another session can also message the child. When the +Agent has no evidence of the sender, it reports the incoming view without +`senderSessionId` and **MUST NOT** assume the parent. It can supply the sender +in a later upsert. Clients display such an entry as a message from another +session, not as a user message. + Agents **SHOULD NOT** report this task as a `user_message_chunk` or a v2 `user_message` in the child's transcript. A user message would attribute the task to the human user. A `session_message` lets Clients render it as @@ -856,15 +882,27 @@ When observability of child foreground work is actually lost, the Agent **MUST** report `unknown` using the existing state payload: v1 sends `subagent_update` with `"state": { "state": "unknown" }` on the parent stream; v2 sends `state_update` with `"state": "unknown"` on the child stream. Silence -alone is not evidence of lost observability. V2 mirrors this snapshot on the +alone is not evidence of lost observability. Observability is also lost when +the Agent itself stops tracking a child that may still be working, for +example when its runtime starts a new conversation and discards the +association while the child's work continues. V2 mirrors this snapshot on the parent association as well. The Client **MUST NOT** continue presenting an earlier `running` or `requires_action` as confirmed live activity after `unknown`, though it may retain that last-known value for context. A subsequent `running`, `requires_action`, or `idle` replaces `unknown` for the same child ID. `unknown` neither closes or cancels the child nor resolves pending requests; it does not automatically revoke mutation capabilities. -If a control is no longer available, the Agent updates `capabilities` -separately. +If a control is no longer available, the Agent updates `capabilities`, in the +same `subagent_update` or a later one. + +`unknown` also leaves the child's unfinished tool calls, terminals, and v2 +`background` list as last reported. The Agent **MUST NOT** invent a final +status for them, and it need not send further updates for work it can no +longer observe. The Client **MUST NOT** present them as confirmed live +activity or treat them as failed or cancelled. It may show them as +unconfirmed, as it does after [connection loss](#connection-loss). If the +Agent later learns that the work ended, it reports the final statuses and the +child's `idle` state as usual. The association's `state` field is a replacement snapshot, not a nested patch: omission leaves the previous snapshot unchanged, `null` clears it, and a @@ -1606,6 +1644,14 @@ important difference from a user-facing session. ## Revision history +- 2026-11-12: Clarified, from adapter implementation + feedback, that an incoming view omits `senderSessionId` when the sender is + not known, when an Agent links two views or leaves them unlinked, and when + it reports an outgoing view. An Agent that stops tracking a child that may + still be working reports `unknown`, which leaves the child's unfinished + tool calls, terminals, and `background` list as last reported. Session + references are resent with replaced tool call content and are added only + for sessions the Agent can resolve. - 2026-11-09: Stated that one-shot children, announced for a single delegation and never reused, conform to this RFD. Named a per-child `prompt` capability as the expected way to let Clients prompt a child in the future. diff --git a/schema/v1/schema.unstable.json b/schema/v1/schema.unstable.json index b24ca2c1f..0f105680b 100644 --- a/schema/v1/schema.unstable.json +++ b/schema/v1/schema.unstable.json @@ -6089,7 +6089,7 @@ } }, "UnknownStateUpdate": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe Agent cannot currently determine foreground activity.\n\nReport this when activity becomes unobservable, not merely because the child\nhas been quiet. The Client MUST stop presenting the previous state as confirmed\ncurrent activity, but may retain it as last known. A later state replaces this\nsnapshot normally.\n\nThis is not a task outcome or session closure. It does not cancel work, resolve\npending requests, or revoke capabilities. Capabilities are updated separately.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe Agent cannot currently determine foreground activity.\n\nReport this when activity becomes unobservable, not merely because the child\nhas been quiet. The Client MUST stop presenting the previous state as confirmed\ncurrent activity, but may retain it as last known. A later state replaces this\nsnapshot normally.\n\nThis is not a task outcome or session closure. It does not cancel work, resolve\npending requests, or revoke capabilities. If a control is no longer available,\nthe Agent updates the capabilities explicitly. Unfinished tool calls and\nterminals stay as last reported. The Agent does not invent a final status\nfor them, and Clients do not present them as confirmed live activity.", "type": "object", "properties": { "_meta": { diff --git a/schema/v2/schema.unstable.json b/schema/v2/schema.unstable.json index 8650366c8..cff7ad10b 100644 --- a/schema/v2/schema.unstable.json +++ b/schema/v2/schema.unstable.json @@ -5599,7 +5599,7 @@ } }, "UnknownStateUpdate": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe Agent cannot currently determine foreground activity.\n\nReport this when activity becomes unobservable, not merely because the session\nhas been quiet. The Client MUST stop presenting the previous state as confirmed\ncurrent activity, but may retain it as last known. A later state replaces this\nsnapshot normally.\n\nThis is not a task outcome or session closure. It does not cancel work, resolve\npending requests, or revoke capabilities. Capabilities are updated separately.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe Agent cannot currently determine foreground activity.\n\nReport this when activity becomes unobservable, not merely because the session\nhas been quiet. The Client MUST stop presenting the previous state as confirmed\ncurrent activity, but may retain it as last known. A later state replaces this\nsnapshot normally.\n\nThis is not a task outcome or session closure. It does not cancel work, resolve\npending requests, or revoke capabilities. If a control is no longer available,\nthe Agent updates the capabilities explicitly. Unfinished tool calls and\nterminals stay as last reported. The Agent does not invent a final status\nfor them, and Clients do not present them as confirmed live activity.", "type": "object", "properties": { "background": {