From 6eb38f80dcaaf1caabf520cce5cd5ae7ed1ba1fc Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Thu, 2 Jul 2026 11:03:58 +0200 Subject: [PATCH] fix(unstable-v2): Preserve meta update signals in v2 --- agent-client-protocol-schema/src/v2/client.rs | 102 ++++++++++++------ .../src/v2/conversion.rs | 35 +++++- .../src/v2/tool_call.rs | 71 +++++++++--- docs/protocol/v2/draft/prompt-lifecycle.mdx | 5 +- docs/protocol/v2/draft/schema.mdx | 77 ++++++------- docs/protocol/v2/draft/session-list.mdx | 8 +- docs/protocol/v2/draft/session-setup.mdx | 2 +- docs/protocol/v2/draft/tool-calls.mdx | 15 +-- docs/protocol/v2/prompt-lifecycle.mdx | 5 +- docs/protocol/v2/schema.mdx | 77 ++++++------- docs/protocol/v2/session-list.mdx | 7 +- docs/protocol/v2/session-setup.mdx | 2 +- docs/protocol/v2/tool-calls.mdx | 15 +-- docs/rfds/session-info-update.mdx | 14 +-- docs/rfds/v2/tool-call-updates.mdx | 1 + schema/v2/schema.json | 24 ++--- schema/v2/schema.unstable.json | 24 ++--- 17 files changed, 299 insertions(+), 185 deletions(-) diff --git a/agent-client-protocol-schema/src/v2/client.rs b/agent-client-protocol-schema/src/v2/client.rs index d43786bdd..2619d71fa 100644 --- a/agent-client-protocol-schema/src/v2/client.rs +++ b/agent-client-protocol-schema/src/v2/client.rs @@ -321,6 +321,9 @@ impl ConfigOptionUpdate { /// /// Agents send this notification to update session information like title or custom metadata. /// This allows clients to display dynamic session names and track session state changes. +/// +/// Omitted fields leave the existing session info unchanged. `null` clears the +/// corresponding value. #[serde_as] #[skip_serializing_none] #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] @@ -338,15 +341,18 @@ pub struct SessionInfoUpdate { #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")] pub updated_at: MaybeUndefined, /// The _meta property is reserved by ACP to allow clients and agents to attach additional - /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. + /// metadata to their interactions. Omitted means no metadata update; `null` is an + /// explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) - #[serde_as(deserialize_as = "DefaultOnError")] + #[serde_as(deserialize_as = "DefaultOnError>")] #[schemars(extend("x-deserialize-default-on-error" = true))] - #[serde(default)] - #[serde(rename = "_meta")] - pub meta: Option, + #[serde( + rename = "_meta", + default, + skip_serializing_if = "MaybeUndefined::is_undefined" + )] + pub meta: MaybeUndefined, } impl SessionInfoUpdate { @@ -371,13 +377,13 @@ impl SessionInfoUpdate { } /// The _meta property is reserved by ACP to allow clients and agents to attach additional - /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. + /// metadata to their interactions. Omitted means no metadata update; `null` is an + /// explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] - pub fn meta(mut self, meta: impl IntoOption) -> Self { - self.meta = meta.into_option(); + pub fn meta(mut self, meta: impl IntoMaybeUndefined) -> Self { + self.meta = meta.into_maybe_undefined(); self } } @@ -736,7 +742,7 @@ impl Cost { } } -/// A streamed item of content +/// A streamed item of message content. #[serde_as] #[skip_serializing_none] #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] @@ -752,7 +758,7 @@ pub struct ContentChunk { pub content: ContentBlock, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. + /// these keys. This field is chunk-scoped. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde_as(deserialize_as = "DefaultOnError")] @@ -775,7 +781,7 @@ impl ContentChunk { /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. + /// these keys. This field is chunk-scoped. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] @@ -787,11 +793,11 @@ impl ContentChunk { /// A user message upsert. /// -/// Only [`UserMessage::message_id`] is required. Other fields have patch -/// semantics: omitted fields leave the existing message value unchanged, `null` -/// clears or unsets the value, and concrete values replace the previous value. -/// For a new `messageId`, omitted fields use client defaults. `content` is -/// replaced as a whole array; send `[]` or `null` to clear it. +/// Only [`UserMessage::message_id`] is required. `content` has patch semantics: +/// an omitted field leaves existing message content unchanged, `null` clears the +/// value, and a concrete array replaces the previous value. For a new +/// `messageId`, omitted fields use client defaults. `content` is replaced as a +/// whole array; send `[]` or `null` to clear it. /// /// Message updates and chunks are applied in the order they are received. When /// a `user_message` update includes `content`, that array replaces any content @@ -813,7 +819,7 @@ pub struct UserMessage { pub content: MaybeUndefined>, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. + /// these keys. Omitted means no metadata update; `null` is an explicit clear signal. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde_as(deserialize_as = "DefaultOnError>")] @@ -858,11 +864,11 @@ impl UserMessage { /// An agent message upsert. /// -/// Only [`AgentMessage::message_id`] is required. Other fields have patch -/// semantics: omitted fields leave the existing message value unchanged, `null` -/// clears or unsets the value, and concrete values replace the previous value. -/// For a new `messageId`, omitted fields use client defaults. `content` is -/// replaced as a whole array; send `[]` or `null` to clear it. +/// Only [`AgentMessage::message_id`] is required. `content` has patch semantics: +/// an omitted field leaves existing message content unchanged, `null` clears the +/// value, and a concrete array replaces the previous value. For a new +/// `messageId`, omitted fields use client defaults. `content` is replaced as a +/// whole array; send `[]` or `null` to clear it. /// /// Message updates and chunks are applied in the order they are received. When /// an `agent_message` update includes `content`, that array replaces any @@ -884,7 +890,7 @@ pub struct AgentMessage { pub content: MaybeUndefined>, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. + /// these keys. Omitted means no metadata update; `null` is an explicit clear signal. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde_as(deserialize_as = "DefaultOnError>")] @@ -929,11 +935,11 @@ impl AgentMessage { /// An agent thought or reasoning message upsert. /// -/// Only [`AgentThought::message_id`] is required. Other fields have patch -/// semantics: omitted fields leave the existing thought value unchanged, `null` -/// clears or unsets the value, and concrete values replace the previous value. -/// For a new `messageId`, omitted fields use client defaults. `content` is -/// replaced as a whole array; send `[]` or `null` to clear it. +/// Only [`AgentThought::message_id`] is required. `content` has patch semantics: +/// an omitted field leaves existing thought content unchanged, `null` clears the +/// value, and a concrete array replaces the previous value. For a new +/// `messageId`, omitted fields use client defaults. `content` is replaced as a +/// whole array; send `[]` or `null` to clear it. /// /// Message updates and chunks are applied in the order they are received. When /// an `agent_thought` update includes `content`, that array replaces any @@ -955,7 +961,7 @@ pub struct AgentThought { pub content: MaybeUndefined>, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. + /// these keys. Omitted means no metadata update; `null` is an explicit clear signal. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde_as(deserialize_as = "DefaultOnError>")] @@ -2104,7 +2110,7 @@ mod tests { SessionInfoUpdate { title: MaybeUndefined::Undefined, updated_at: MaybeUndefined::Undefined, - meta: None + meta: MaybeUndefined::Undefined } ); assert_eq!( @@ -2113,7 +2119,7 @@ mod tests { SessionInfoUpdate { title: MaybeUndefined::Null, updated_at: MaybeUndefined::Null, - meta: None + meta: MaybeUndefined::Undefined } ); assert_eq!( @@ -2124,14 +2130,42 @@ mod tests { SessionInfoUpdate { title: MaybeUndefined::Value("title".to_string()), updated_at: MaybeUndefined::Value("timestamp".to_string()), - meta: None + meta: MaybeUndefined::Undefined } ); + let clear_meta = + serde_json::from_value::(json!({"_meta": null})).unwrap(); + assert_eq!(clear_meta.meta, MaybeUndefined::Null); + + let mut meta = Meta::new(); + meta.insert("source".to_string(), json!("session-info")); + + assert_eq!( + serde_json::from_value::(json!({"_meta": { + "source": "session-info" + }})) + .unwrap() + .meta, + MaybeUndefined::Value(meta.clone()) + ); + assert_eq!( serde_json::to_value(SessionInfoUpdate::new()).unwrap(), json!({}) ); + + assert_eq!( + serde_json::to_value(SessionInfoUpdate::new().meta(None::)).unwrap(), + json!({"_meta": null}) + ); + + assert_eq!( + serde_json::to_value(SessionInfoUpdate::new().meta(meta)).unwrap(), + json!({"_meta": { + "source": "session-info" + }}) + ); assert_eq!( serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(), json!({"title": "title"}) diff --git a/agent-client-protocol-schema/src/v2/conversion.rs b/agent-client-protocol-schema/src/v2/conversion.rs index c907392e0..fe0fae95e 100644 --- a/agent-client-protocol-schema/src/v2/conversion.rs +++ b/agent-client-protocol-schema/src/v2/conversion.rs @@ -1235,7 +1235,7 @@ impl IntoV1 for super::SessionInfoUpdate { Ok(crate::v1::SessionInfoUpdate { title: title.into_v1()?, updated_at: updated_at.into_v1()?, - meta: meta.into_v1()?, + meta: maybe_undefined_meta_into_v1_option("SessionInfoUpdate", meta)?, }) } } @@ -1252,7 +1252,7 @@ impl IntoV2 for crate::v1::SessionInfoUpdate { Ok(super::SessionInfoUpdate { title: title.into_v2()?, updated_at: updated_at.into_v2()?, - meta: meta.into_v2()?, + meta: option_into_v2_maybe_undefined(meta)?, }) } } @@ -2380,6 +2380,19 @@ where } } +fn maybe_undefined_meta_into_v1_option( + context: &str, + value: crate::MaybeUndefined, +) -> Result> { + match value { + crate::MaybeUndefined::Value(value) => Ok(Some(value.into_v1()?)), + crate::MaybeUndefined::Null => Err(ProtocolConversionError::new(format!( + "v2 {context} with null _meta cannot be represented in v1" + ))), + crate::MaybeUndefined::Undefined => Ok(None), + } +} + fn option_into_v2_maybe_undefined(value: Option) -> Result> where T: IntoV2, @@ -2441,7 +2454,7 @@ impl IntoV1 for super::ToolCallUpdate { raw_input: maybe_undefined_value_into_v1_option(raw_input), raw_output: maybe_undefined_value_into_v1_option(raw_output), }, - meta: meta.into_v1()?, + meta: maybe_undefined_meta_into_v1_option("ToolCallUpdate", meta)?, }) } } @@ -2478,7 +2491,7 @@ impl IntoV2 for crate::v1::ToolCall { locations: vec_into_v2_maybe_undefined_skip_errors(locations), raw_input: option_into_v2_maybe_undefined(raw_input)?, raw_output: option_into_v2_maybe_undefined(raw_output)?, - meta: meta.into_v2()?, + meta: option_into_v2_maybe_undefined(meta)?, }) } } @@ -2510,7 +2523,7 @@ impl IntoV2 for crate::v1::ToolCallUpdate { locations: option_vec_into_v2_maybe_undefined_skip_errors(locations), raw_input: option_into_v2_maybe_undefined(raw_input)?, raw_output: option_into_v2_maybe_undefined(raw_output)?, - meta: meta.into_v2()?, + meta: option_into_v2_maybe_undefined(meta)?, }) } } @@ -9917,6 +9930,18 @@ mod tests { assert_json_eq_after_v1_to_v2::(update); } + #[test] + fn v2_entity_meta_null_does_not_convert_to_v1() { + assert_v2_to_v1_error( + v2::SessionInfoUpdate::new().meta(None::), + "v2 SessionInfoUpdate with null _meta cannot be represented in v1", + ); + assert_v2_to_v1_error( + v2::ToolCallUpdate::new("tc").meta(None::), + "v2 ToolCallUpdate with null _meta cannot be represented in v1", + ); + } + #[test] fn round_trips_session_notification_for_unchanged_update_kinds() { fn content_chunk(text: &str, message_id: &str) -> v1::ContentChunk { diff --git a/agent-client-protocol-schema/src/v2/tool_call.rs b/agent-client-protocol-schema/src/v2/tool_call.rs index 0d9ba9773..463cf30ba 100644 --- a/agent-client-protocol-schema/src/v2/tool_call.rs +++ b/agent-client-protocol-schema/src/v2/tool_call.rs @@ -73,15 +73,18 @@ pub struct ToolCallUpdate { #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")] pub raw_output: MaybeUndefined, /// The _meta property is reserved by ACP to allow clients and agents to attach additional - /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. + /// metadata to their interactions. Omitted means no metadata update; `null` is an + /// explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) - #[serde_as(deserialize_as = "DefaultOnError")] + #[serde_as(deserialize_as = "DefaultOnError>")] #[schemars(extend("x-deserialize-default-on-error" = true))] - #[serde(default)] - #[serde(rename = "_meta")] - pub meta: Option, + #[serde( + rename = "_meta", + default, + skip_serializing_if = "MaybeUndefined::is_undefined" + )] + pub meta: MaybeUndefined, } impl ToolCallUpdate { @@ -97,7 +100,7 @@ impl ToolCallUpdate { locations: MaybeUndefined::Undefined, raw_input: MaybeUndefined::Undefined, raw_output: MaybeUndefined::Undefined, - meta: None, + meta: MaybeUndefined::Undefined, } } @@ -153,13 +156,13 @@ impl ToolCallUpdate { } /// The _meta property is reserved by ACP to allow clients and agents to attach additional - /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. + /// metadata to their interactions. Omitted means no metadata update; `null` is an + /// explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] - pub fn meta(mut self, meta: impl IntoOption) -> Self { - self.meta = meta.into_option(); + pub fn meta(mut self, meta: impl IntoMaybeUndefined) -> Self { + self.meta = meta.into_maybe_undefined(); self } @@ -190,6 +193,9 @@ impl ToolCallUpdate { if !update.raw_output.is_undefined() { self.raw_output = update.raw_output; } + if !update.meta.is_undefined() { + self.meta = update.meta; + } } } @@ -211,8 +217,7 @@ pub struct ToolCallContentChunk { pub content: ToolCallContent, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. This field is optional; omitted or `null` means there is no - /// chunk-level metadata. + /// these keys. This field is chunk-scoped. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde_as(deserialize_as = "DefaultOnError")] @@ -235,7 +240,7 @@ impl ToolCallContentChunk { /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. + /// these keys. This field is chunk-scoped. /// /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] @@ -654,6 +659,44 @@ mod tests { assert_eq!(deserialized.locations, MaybeUndefined::Value(Vec::new())); } + #[test] + fn tool_call_update_distinguishes_meta_omitted_null_and_value() { + let mut meta = Meta::new(); + meta.insert("source".to_string(), serde_json::json!("tool-call")); + + assert_eq!( + serde_json::to_value(ToolCallUpdate::new("tc_1").meta(meta.clone())).unwrap(), + serde_json::json!({ + "toolCallId": "tc_1", + "_meta": { + "source": "tool-call" + } + }) + ); + + assert_eq!( + serde_json::to_value(ToolCallUpdate::new("tc_1").meta(None::)).unwrap(), + serde_json::json!({ + "toolCallId": "tc_1", + "_meta": null + }) + ); + + let deserialized: ToolCallUpdate = serde_json::from_value(serde_json::json!({ + "toolCallId": "tc_1", + "_meta": null + })) + .unwrap(); + assert_eq!(deserialized.meta, MaybeUndefined::Null); + + let patch = ToolCallUpdate::new("tc_1"); + assert_eq!(patch.meta, MaybeUndefined::Undefined); + + let mut stored = ToolCallUpdate::new("tc_1").meta(meta); + stored.apply_update(ToolCallUpdate::new("tc_1").meta(None::)); + assert_eq!(stored.meta, MaybeUndefined::Null); + } + #[test] fn tool_call_update_skips_malformed_list_items() { let deserialized: ToolCallUpdate = serde_json::from_value(serde_json::json!({ diff --git a/docs/protocol/v2/draft/prompt-lifecycle.mdx b/docs/protocol/v2/draft/prompt-lifecycle.mdx index 51bad0bb5..4a4494d35 100644 --- a/docs/protocol/v2/draft/prompt-lifecycle.mdx +++ b/docs/protocol/v2/draft/prompt-lifecycle.mdx @@ -227,14 +227,15 @@ The Agent can report the model's text response as an `agent_message` update with The Agent **MUST** include an opaque `messageId` on message updates and message chunks. -User, agent, and thought messages can each be reported either as a message update with a full `content` array or as streamed chunks. `user_message`, `agent_message`, and `agent_thought` updates are upserts keyed by `messageId`: omitted fields leave the existing message unchanged, `null` clears a field, and concrete values replace the previous value. `content` arrays replace the whole message content; send `[]` or `null` to clear content. Chunk updates with the same `messageId` append content; a changed `messageId` indicates a new message. +User, agent, and thought messages can each be reported either as a message update with a full `content` array or as streamed chunks. `user_message`, `agent_message`, and `agent_thought` updates are upserts keyed by `messageId`: omitted `content` leaves the existing message content unchanged, `content: null` clears it, and concrete `content` arrays replace the previous content. Chunk updates with the same `messageId` append content; a changed `messageId` indicates a new message. Clients apply message updates and chunks in the order they are received for each `messageId`: -- A message update without `content` leaves the current content unchanged, so Agents can update `_meta` or future optional fields without resending content. +- A message update without `content` leaves the current content unchanged, so Agents can update other optional fields without resending content. - A message update with `content` replaces all content currently stored for that message, including content accumulated from earlier chunks. - A message update with `content: []` or `content: null` clears the message content. - A chunk appends its `content` to whatever content is current for that message, whether that content came from an earlier message update or earlier chunks. +- A chunk's `_meta`, when present, is chunk-scoped. For example, if an Agent sends `agent_message` with `content: [A]`, then sends `agent_message_chunk` with `B`, the rendered message content is `[A, B]`. If it later sends another `agent_message` with `content: [C]`, the rendered content becomes `[C]`; the earlier full content and chunks are replaced. Subsequent chunks append to `[C]`. diff --git a/docs/protocol/v2/draft/schema.mdx b/docs/protocol/v2/draft/schema.mdx index 86f5d0db9..e7e072548 100644 --- a/docs/protocol/v2/draft/schema.mdx +++ b/docs/protocol/v2/draft/schema.mdx @@ -2242,11 +2242,11 @@ baseline session methods: `session/new`, `session/prompt`, An agent message upsert. -Only `AgentMessage::message_id` is required. Other fields have patch -semantics: omitted fields leave the existing message value unchanged, `null` -clears or unsets the value, and concrete values replace the previous value. -For a new `messageId`, omitted fields use client defaults. `content` is -replaced as a whole array; send `[]` or `null` to clear it. +Only `AgentMessage::message_id` is required. `content` has patch semantics: +an omitted field leaves existing message content unchanged, `null` clears the +value, and a concrete array replaces the previous value. For a new +`messageId`, omitted fields use client defaults. `content` is replaced as a +whole array; send `[]` or `null` to clear it. Message updates and chunks are applied in the order they are received. When an `agent_message` update includes `content`, that array replaces any @@ -2261,7 +2261,7 @@ content. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -2277,11 +2277,11 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d An agent thought or reasoning message upsert. -Only `AgentThought::message_id` is required. Other fields have patch -semantics: omitted fields leave the existing thought value unchanged, `null` -clears or unsets the value, and concrete values replace the previous value. -For a new `messageId`, omitted fields use client defaults. `content` is -replaced as a whole array; send `[]` or `null` to clear it. +Only `AgentThought::message_id` is required. `content` has patch semantics: +an omitted field leaves existing thought content unchanged, `null` clears the +value, and a concrete array replaces the previous value. For a new +`messageId`, omitted fields use client defaults. `content` is replaced as a +whole array; send `[]` or `null` to clear it. Message updates and chunks are applied in the order they are received. When an `agent_thought` update includes `content`, that array replaces any @@ -2296,7 +2296,7 @@ content. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -3205,7 +3205,7 @@ future ACP variants. ## ContentChunk -A streamed item of content +A streamed item of message content. **Type:** Object @@ -3214,7 +3214,7 @@ A streamed item of content The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -7276,14 +7276,17 @@ Update to session metadata. All fields are optional to support partial updates. Agents send this notification to update session information like title or custom metadata. This allows clients to display dynamic session names and track session state changes. +Omitted fields leave the existing session info unchanged. `null` clears the +corresponding value. + **Type:** Object **Properties:** The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +metadata to their interactions. Omitted means no metadata update; `null` is an +explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -7370,7 +7373,7 @@ A chunk of the user's message being streamed. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -7404,7 +7407,7 @@ fields in the new update patch the previous fields for that message. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -7430,7 +7433,7 @@ A chunk of the agent's response being streamed. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -7464,7 +7467,7 @@ fields in the new update patch the previous fields for that message. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -7490,7 +7493,7 @@ A chunk of the agent's internal reasoning being streamed. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -7524,7 +7527,7 @@ fields in the new update patch the previous fields for that message. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -7566,8 +7569,7 @@ A chunk of tool-call content being streamed. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. This field is optional; omitted or `null` means there is no -chunk-level metadata. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -7592,8 +7594,8 @@ A tool call has been created or updated. The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +metadata to their interactions. Omitted means no metadata update; `null` is an +explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -7735,8 +7737,8 @@ Session metadata has been updated (title, timestamps, custom metadata) The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +metadata to their interactions. Omitted means no metadata update; `null` is an +explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -8328,8 +8330,7 @@ collection instead. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. This field is optional; omitted or `null` means there is no -chunk-level metadata. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -8436,8 +8437,8 @@ See protocol docs: [Tool Calls](https://agentclientprotocol.com/protocol/v2/draf The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +metadata to their interactions. Omitted means no metadata update; `null` is an +explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) @@ -8622,11 +8623,11 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d A user message upsert. -Only `UserMessage::message_id` is required. Other fields have patch -semantics: omitted fields leave the existing message value unchanged, `null` -clears or unsets the value, and concrete values replace the previous value. -For a new `messageId`, omitted fields use client defaults. `content` is -replaced as a whole array; send `[]` or `null` to clear it. +Only `UserMessage::message_id` is required. `content` has patch semantics: +an omitted field leaves existing message content unchanged, `null` clears the +value, and a concrete array replaces the previous value. For a new +`messageId`, omitted fields use client defaults. `content` is replaced as a +whole array; send `[]` or `null` to clear it. Message updates and chunks are applied in the order they are received. When a `user_message` update includes `content`, that array replaces any content @@ -8641,7 +8642,7 @@ content. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) diff --git a/docs/protocol/v2/draft/session-list.mdx b/docs/protocol/v2/draft/session-list.mdx index d810134fb..f5e662c71 100644 --- a/docs/protocol/v2/draft/session-list.mdx +++ b/docs/protocol/v2/draft/session-list.mdx @@ -196,7 +196,7 @@ Agents can update session metadata in real-time by sending a `session_info_updat } ``` -All fields are optional. Only include fields that have changed — omitted fields are left unchanged. +All fields are optional. Only include fields that have changed; omitted fields are left unchanged. Set a nullable field to `null` to clear it. Human-readable title for the session. Set to `null` to clear. @@ -206,9 +206,9 @@ All fields are optional. Only include fields that have changed — omitted field ISO 8601 timestamp of last activity. Set to `null` to clear. - - Agent-specific metadata. See - [Extensibility](/protocol/v2/draft/extensibility). + + Agent-specific metadata update. Omit to leave unchanged; set to `null` to + clear. See [Extensibility](/protocol/v2/draft/extensibility). The `sessionId`, `cwd`, and `additionalDirectories` fields are **not** included diff --git a/docs/protocol/v2/draft/session-setup.mdx b/docs/protocol/v2/draft/session-setup.mdx index 6a503c399..e729a9523 100644 --- a/docs/protocol/v2/draft/session-setup.mdx +++ b/docs/protocol/v2/draft/session-setup.mdx @@ -185,7 +185,7 @@ Followed by the agent's response: During replay, the Agent **MUST** include an opaque, unique `messageId` for each replayed message. `user_message`, `agent_message`, and `agent_thought` updates are upserts keyed by `messageId`; their `content` arrays replace the whole message content, while chunk updates with the same `messageId` append content. -Clients apply replayed message updates and chunks in the order they are received. If a message update with `content` arrives after chunks for the same `messageId`, it replaces the content accumulated from those chunks. If chunks arrive after a message update, they append to that update's current content. Message updates that omit `content` can update `_meta` or future fields without changing the current content. +Clients apply replayed message updates and chunks in the order they are received. If a message update with `content` arrives after chunks for the same `messageId`, it replaces the content accumulated from those chunks. If chunks arrive after a message update, they append to that update's current content. Message updates that omit `content` can update other optional fields without changing the current content. When **all** the conversation entries have been reported to the Client, the Agent **MUST** respond to the original `session/load` request. diff --git a/docs/protocol/v2/draft/tool-calls.mdx b/docs/protocol/v2/draft/tool-calls.mdx index c146d5173..baec689f5 100644 --- a/docs/protocol/v2/draft/tool-calls.mdx +++ b/docs/protocol/v2/draft/tool-calls.mdx @@ -79,11 +79,13 @@ Custom or future tool kinds can be used when Clients can fall back to generic to The `tool_call_update` notification is an upsert keyed by `toolCallId`. For -existing tool calls, omitted fields leave the previous value unchanged, `null` -explicitly clears or unsets the field, and concrete values replace the previous -value. For a new `toolCallId`, omitted fields use Client defaults. `content` and -`locations` are replaced as whole arrays; send `[]` or `null` to clear them. -Use `tool_call_content_chunk` when a tool produces content incrementally and the +existing tool calls, fields other than `_meta` leave the previous value +unchanged when omitted, explicitly clear or unset the value when `null`, and +replace the previous value when concrete values are sent. For a new +`toolCallId`, omitted fields use Client defaults. `content` and `locations` are +replaced as whole arrays; send `[]` or `null` to clear them. For `_meta`, omit +the field to leave it unchanged or set it to `null` to clear it. Use +`tool_call_content_chunk` when a tool produces content incrementally and the Client should append each item instead of replacing the whole `content` collection. @@ -157,7 +159,8 @@ the order they are received for each `toolCallId`. A content. A later `tool_call_update` with `content` replaces all content currently stored for that tool call, including content accumulated from earlier chunks. Later chunks append to that replacement content. A `tool_call_update` -with `content: []` or `content: null` clears the tool-call content. +with `content: []` or `content: null` clears the tool-call content. A +`tool_call_content_chunk`'s `_meta`, when present, is chunk-scoped. ## Requesting Permission diff --git a/docs/protocol/v2/prompt-lifecycle.mdx b/docs/protocol/v2/prompt-lifecycle.mdx index 0185edd3d..214a46250 100644 --- a/docs/protocol/v2/prompt-lifecycle.mdx +++ b/docs/protocol/v2/prompt-lifecycle.mdx @@ -229,14 +229,15 @@ The Agent can report the model's text response as an `agent_message` update with The Agent **MUST** include an opaque `messageId` on message updates and message chunks. -User, agent, and thought messages can each be reported either as a message update with a full `content` array or as streamed chunks. `user_message`, `agent_message`, and `agent_thought` updates are upserts keyed by `messageId`: omitted fields leave the existing message unchanged, `null` clears a field, and concrete values replace the previous value. `content` arrays replace the whole message content; send `[]` or `null` to clear content. Chunk updates with the same `messageId` append content; a changed `messageId` indicates a new message. +User, agent, and thought messages can each be reported either as a message update with a full `content` array or as streamed chunks. `user_message`, `agent_message`, and `agent_thought` updates are upserts keyed by `messageId`: omitted `content` leaves the existing message content unchanged, `content: null` clears it, and concrete `content` arrays replace the previous content. Chunk updates with the same `messageId` append content; a changed `messageId` indicates a new message. Clients apply message updates and chunks in the order they are received for each `messageId`: -- A message update without `content` leaves the current content unchanged, so Agents can update `_meta` or future optional fields without resending content. +- A message update without `content` leaves the current content unchanged, so Agents can update other optional fields without resending content. - A message update with `content` replaces all content currently stored for that message, including content accumulated from earlier chunks. - A message update with `content: []` or `content: null` clears the message content. - A chunk appends its `content` to whatever content is current for that message, whether that content came from an earlier message update or earlier chunks. +- A chunk's `_meta`, when present, is chunk-scoped. For example, if an Agent sends `agent_message` with `content: [A]`, then sends `agent_message_chunk` with `B`, the rendered message content is `[A, B]`. If it later sends another `agent_message` with `content: [C]`, the rendered content becomes `[C]`; the earlier full content and chunks are replaced. Subsequent chunks append to `[C]`. diff --git a/docs/protocol/v2/schema.mdx b/docs/protocol/v2/schema.mdx index 9d4ea2b6b..ea75ff15f 100644 --- a/docs/protocol/v2/schema.mdx +++ b/docs/protocol/v2/schema.mdx @@ -973,11 +973,11 @@ baseline session methods: `session/new`, `session/prompt`, An agent message upsert. -Only `AgentMessage::message_id` is required. Other fields have patch -semantics: omitted fields leave the existing message value unchanged, `null` -clears or unsets the value, and concrete values replace the previous value. -For a new `messageId`, omitted fields use client defaults. `content` is -replaced as a whole array; send `[]` or `null` to clear it. +Only `AgentMessage::message_id` is required. `content` has patch semantics: +an omitted field leaves existing message content unchanged, `null` clears the +value, and a concrete array replaces the previous value. For a new +`messageId`, omitted fields use client defaults. `content` is replaced as a +whole array; send `[]` or `null` to clear it. Message updates and chunks are applied in the order they are received. When an `agent_message` update includes `content`, that array replaces any @@ -992,7 +992,7 @@ content. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -1008,11 +1008,11 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e An agent thought or reasoning message upsert. -Only `AgentThought::message_id` is required. Other fields have patch -semantics: omitted fields leave the existing thought value unchanged, `null` -clears or unsets the value, and concrete values replace the previous value. -For a new `messageId`, omitted fields use client defaults. `content` is -replaced as a whole array; send `[]` or `null` to clear it. +Only `AgentThought::message_id` is required. `content` has patch semantics: +an omitted field leaves existing thought content unchanged, `null` clears the +value, and a concrete array replaces the previous value. For a new +`messageId`, omitted fields use client defaults. `content` is replaced as a +whole array; send `[]` or `null` to clear it. Message updates and chunks are applied in the order they are received. When an `agent_thought` update includes `content`, that array replaces any @@ -1027,7 +1027,7 @@ content. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -1610,7 +1610,7 @@ future ACP variants. ## ContentChunk -A streamed item of content +A streamed item of message content. **Type:** Object @@ -1619,7 +1619,7 @@ A streamed item of content The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -3276,14 +3276,17 @@ Update to session metadata. All fields are optional to support partial updates. Agents send this notification to update session information like title or custom metadata. This allows clients to display dynamic session names and track session state changes. +Omitted fields leave the existing session info unchanged. `null` clears the +corresponding value. + **Type:** Object **Properties:** The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +metadata to their interactions. Omitted means no metadata update; `null` is an +explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -3370,7 +3373,7 @@ A chunk of the user's message being streamed. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -3404,7 +3407,7 @@ fields in the new update patch the previous fields for that message. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -3430,7 +3433,7 @@ A chunk of the agent's response being streamed. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -3464,7 +3467,7 @@ fields in the new update patch the previous fields for that message. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -3490,7 +3493,7 @@ A chunk of the agent's internal reasoning being streamed. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -3524,7 +3527,7 @@ fields in the new update patch the previous fields for that message. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -3566,8 +3569,7 @@ A chunk of tool-call content being streamed. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. This field is optional; omitted or `null` means there is no -chunk-level metadata. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -3592,8 +3594,8 @@ A tool call has been created or updated. The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +metadata to their interactions. Omitted means no metadata update; `null` is an +explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -3708,8 +3710,8 @@ Session metadata has been updated (title, timestamps, custom metadata) The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +metadata to their interactions. Omitted means no metadata update; `null` is an +explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -4104,8 +4106,7 @@ collection instead. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. This field is optional; omitted or `null` means there is no -chunk-level metadata. +these keys. This field is chunk-scoped. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -4212,8 +4213,8 @@ See protocol docs: [Tool Calls](https://agentclientprotocol.com/protocol/v2/tool The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +metadata to their interactions. Omitted means no metadata update; `null` is an +explicit clear signal. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) @@ -4341,11 +4342,11 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e A user message upsert. -Only `UserMessage::message_id` is required. Other fields have patch -semantics: omitted fields leave the existing message value unchanged, `null` -clears or unsets the value, and concrete values replace the previous value. -For a new `messageId`, omitted fields use client defaults. `content` is -replaced as a whole array; send `[]` or `null` to clear it. +Only `UserMessage::message_id` is required. `content` has patch semantics: +an omitted field leaves existing message content unchanged, `null` clears the +value, and a concrete array replaces the previous value. For a new +`messageId`, omitted fields use client defaults. `content` is replaced as a +whole array; send `[]` or `null` to clear it. Message updates and chunks are applied in the order they are received. When a `user_message` update includes `content`, that array replaces any content @@ -4360,7 +4361,7 @@ content. The _meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. +these keys. Omitted means no metadata update; `null` is an explicit clear signal. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) diff --git a/docs/protocol/v2/session-list.mdx b/docs/protocol/v2/session-list.mdx index ab003c640..8eb5b4e67 100644 --- a/docs/protocol/v2/session-list.mdx +++ b/docs/protocol/v2/session-list.mdx @@ -196,7 +196,7 @@ Agents can update session metadata in real-time by sending a `session_info_updat } ``` -All fields are optional. Only include fields that have changed — omitted fields are left unchanged. +All fields are optional. Only include fields that have changed; omitted fields are left unchanged. Set a nullable field to `null` to clear it. Human-readable title for the session. Set to `null` to clear. @@ -206,8 +206,9 @@ All fields are optional. Only include fields that have changed — omitted field ISO 8601 timestamp of last activity. Set to `null` to clear. - - Agent-specific metadata. See [Extensibility](/protocol/v2/extensibility). + + Agent-specific metadata update. Omit to leave unchanged; set to `null` to + clear. See [Extensibility](/protocol/v2/extensibility). The `sessionId`, `cwd`, and `additionalDirectories` fields are **not** included diff --git a/docs/protocol/v2/session-setup.mdx b/docs/protocol/v2/session-setup.mdx index dc3d86591..0d5b498ec 100644 --- a/docs/protocol/v2/session-setup.mdx +++ b/docs/protocol/v2/session-setup.mdx @@ -183,7 +183,7 @@ Followed by the agent's response: During replay, the Agent **MUST** include an opaque, unique `messageId` for each replayed message. `user_message`, `agent_message`, and `agent_thought` updates are upserts keyed by `messageId`; their `content` arrays replace the whole message content, while chunk updates with the same `messageId` append content. -Clients apply replayed message updates and chunks in the order they are received. If a message update with `content` arrives after chunks for the same `messageId`, it replaces the content accumulated from those chunks. If chunks arrive after a message update, they append to that update's current content. Message updates that omit `content` can update `_meta` or future fields without changing the current content. +Clients apply replayed message updates and chunks in the order they are received. If a message update with `content` arrives after chunks for the same `messageId`, it replaces the content accumulated from those chunks. If chunks arrive after a message update, they append to that update's current content. Message updates that omit `content` can update other optional fields without changing the current content. When **all** the conversation entries have been reported to the Client, the Agent **MUST** respond to the original `session/load` request. diff --git a/docs/protocol/v2/tool-calls.mdx b/docs/protocol/v2/tool-calls.mdx index 71c1bcfa8..48ea80a50 100644 --- a/docs/protocol/v2/tool-calls.mdx +++ b/docs/protocol/v2/tool-calls.mdx @@ -79,11 +79,13 @@ Custom or future tool kinds can be used when Clients can fall back to generic to The `tool_call_update` notification is an upsert keyed by `toolCallId`. For -existing tool calls, omitted fields leave the previous value unchanged, `null` -explicitly clears or unsets the field, and concrete values replace the previous -value. For a new `toolCallId`, omitted fields use Client defaults. `content` and -`locations` are replaced as whole arrays; send `[]` or `null` to clear them. -Use `tool_call_content_chunk` when a tool produces content incrementally and the +existing tool calls, fields other than `_meta` leave the previous value +unchanged when omitted, explicitly clear or unset the value when `null`, and +replace the previous value when concrete values are sent. For a new +`toolCallId`, omitted fields use Client defaults. `content` and `locations` are +replaced as whole arrays; send `[]` or `null` to clear them. For `_meta`, omit +the field to leave it unchanged or set it to `null` to clear it. Use +`tool_call_content_chunk` when a tool produces content incrementally and the Client should append each item instead of replacing the whole `content` collection. @@ -157,7 +159,8 @@ the order they are received for each `toolCallId`. A content. A later `tool_call_update` with `content` replaces all content currently stored for that tool call, including content accumulated from earlier chunks. Later chunks append to that replacement content. A `tool_call_update` -with `content: []` or `content: null` clears the tool-call content. +with `content: []` or `content: null` clears the tool-call content. A +`tool_call_content_chunk`'s `_meta`, when present, is chunk-scoped. ## Requesting Permission diff --git a/docs/rfds/session-info-update.mdx b/docs/rfds/session-info-update.mdx index f25ab60bc..6de0fe831 100644 --- a/docs/rfds/session-info-update.mdx +++ b/docs/rfds/session-info-update.mdx @@ -86,7 +86,7 @@ The update type mirrors `SessionInfo` but with all fields optional: sessionUpdate: "session_info_update", title?: string | null, // Update or clear the title updatedAt?: string | null, // ISO 8601 timestamp (usually agent sets this) - _meta?: object | null // Custom metadata (merged with existing) + _meta?: object | null // Custom metadata update signal } ``` @@ -220,7 +220,7 @@ Add to `SessionUpdate` oneOf: 2. **Create documentation** in `/docs/protocol/session-metadata.mdx`: - Explain the notification mechanism - Provide examples of common patterns - - Document merge semantics for `_meta` + - Document omitted, `null`, and object semantics for `_meta` - Clarify relationship with `session/list` 3. **Update existing docs**: @@ -278,9 +278,9 @@ Add to `SessionUpdate` oneOf: **How do `_meta` updates work?** -- **Merge semantics**: New `_meta` fields are merged with existing ones -- To clear a specific field: `"_meta": { "fieldName": null }` -- To clear all custom metadata: `"_meta": null` +- Omitted `_meta` means no metadata update was supplied. +- `"_meta": null` is an explicit clear signal. +- `"_meta": { ... }` supplies a metadata object for clients and extensions to interpret. ### Security Considerations @@ -319,7 +319,7 @@ A future RFD could add explicit request/response for this if needed. ### What if multiple updates are sent in quick succession? -Clients should apply updates incrementally in order. Each notification represents a delta, not a full replacement (except for fields explicitly set to `null`). +Clients should process updates in order and apply each field according to its field semantics. ### Should `updatedAt` be automatically set by the agent? @@ -345,7 +345,7 @@ Clients SHOULD handle long titles gracefully (truncate in UI, show tooltip, etc. ### Can `_meta` have nested objects? -Yes, `_meta` is an arbitrary object. Agents define its structure. The merge semantics apply recursively - nested objects are merged, not replaced entirely. +Yes, `_meta` is an arbitrary object. Agents define its structure. ### What alternative approaches did you consider, and why did you settle on this one? diff --git a/docs/rfds/v2/tool-call-updates.mdx b/docs/rfds/v2/tool-call-updates.mdx index 9233978a2..a08412178 100644 --- a/docs/rfds/v2/tool-call-updates.mdx +++ b/docs/rfds/v2/tool-call-updates.mdx @@ -53,6 +53,7 @@ The v2 `ToolCallUpdate` payload has the following semantics: - Any concrete value replaces the previous value. - `content` and `locations` are replaced as whole arrays, not appended. - `[]` and `null` both clear a collection field. +- `_meta` preserves omitted, `null`, and object values as distinct update signals. - For a new `toolCallId`, omitted fields use Client defaults. - Agents **SHOULD** include `title` the first time they report a `toolCallId`. diff --git a/schema/v2/schema.json b/schema/v2/schema.json index b8f9372fb..e7186c4ba 100644 --- a/schema/v2/schema.json +++ b/schema/v2/schema.json @@ -645,7 +645,7 @@ "x-deserialize-default-on-error": true }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted means no metadata update; `null` is an\nexplicit clear signal. Implementations MUST NOT make assumptions about values at these keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -3199,7 +3199,7 @@ "type": "string" }, "ContentChunk": { - "description": "A streamed item of content", + "description": "A streamed item of message content.", "type": "object", "properties": { "messageId": { @@ -3219,7 +3219,7 @@ ] }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. This field is chunk-scoped.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -3228,7 +3228,7 @@ "required": ["messageId", "content"] }, "UserMessage": { - "description": "A user message upsert.\n\nOnly [`UserMessage::message_id`] is required. Other fields have patch\nsemantics: omitted fields leave the existing message value unchanged, `null`\nclears or unsets the value, and concrete values replace the previous value.\nFor a new `messageId`, omitted fields use client defaults. `content` is\nreplaced as a whole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\na `user_message` update includes `content`, that array replaces any content\npreviously accumulated for the message, including content from earlier\nchunks. Later chunks with the same `messageId` append to the current\ncontent.", + "description": "A user message upsert.\n\nOnly [`UserMessage::message_id`] is required. `content` has patch semantics:\nan omitted field leaves existing message content unchanged, `null` clears the\nvalue, and a concrete array replaces the previous value. For a new\n`messageId`, omitted fields use client defaults. `content` is replaced as a\nwhole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\na `user_message` update includes `content`, that array replaces any content\npreviously accumulated for the message, including content from earlier\nchunks. Later chunks with the same `messageId` append to the current\ncontent.", "type": "object", "properties": { "messageId": { @@ -3249,7 +3249,7 @@ "x-deserialize-skip-invalid-items": true }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. Omitted means no metadata update; `null` is an explicit clear signal.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -3258,7 +3258,7 @@ "required": ["messageId"] }, "AgentMessage": { - "description": "An agent message upsert.\n\nOnly [`AgentMessage::message_id`] is required. Other fields have patch\nsemantics: omitted fields leave the existing message value unchanged, `null`\nclears or unsets the value, and concrete values replace the previous value.\nFor a new `messageId`, omitted fields use client defaults. `content` is\nreplaced as a whole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\nan `agent_message` update includes `content`, that array replaces any\ncontent previously accumulated for the message, including content from\nearlier chunks. Later chunks with the same `messageId` append to the current\ncontent.", + "description": "An agent message upsert.\n\nOnly [`AgentMessage::message_id`] is required. `content` has patch semantics:\nan omitted field leaves existing message content unchanged, `null` clears the\nvalue, and a concrete array replaces the previous value. For a new\n`messageId`, omitted fields use client defaults. `content` is replaced as a\nwhole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\nan `agent_message` update includes `content`, that array replaces any\ncontent previously accumulated for the message, including content from\nearlier chunks. Later chunks with the same `messageId` append to the current\ncontent.", "type": "object", "properties": { "messageId": { @@ -3279,7 +3279,7 @@ "x-deserialize-skip-invalid-items": true }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. Omitted means no metadata update; `null` is an explicit clear signal.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -3288,7 +3288,7 @@ "required": ["messageId"] }, "AgentThought": { - "description": "An agent thought or reasoning message upsert.\n\nOnly [`AgentThought::message_id`] is required. Other fields have patch\nsemantics: omitted fields leave the existing thought value unchanged, `null`\nclears or unsets the value, and concrete values replace the previous value.\nFor a new `messageId`, omitted fields use client defaults. `content` is\nreplaced as a whole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\nan `agent_thought` update includes `content`, that array replaces any\ncontent previously accumulated for the thought, including content from\nearlier chunks. Later chunks with the same `messageId` append to the current\ncontent.", + "description": "An agent thought or reasoning message upsert.\n\nOnly [`AgentThought::message_id`] is required. `content` has patch semantics:\nan omitted field leaves existing thought content unchanged, `null` clears the\nvalue, and a concrete array replaces the previous value. For a new\n`messageId`, omitted fields use client defaults. `content` is replaced as a\nwhole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\nan `agent_thought` update includes `content`, that array replaces any\ncontent previously accumulated for the thought, including content from\nearlier chunks. Later chunks with the same `messageId` append to the current\ncontent.", "type": "object", "properties": { "messageId": { @@ -3309,7 +3309,7 @@ "x-deserialize-skip-invalid-items": true }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. Omitted means no metadata update; `null` is an explicit clear signal.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -3524,7 +3524,7 @@ ] }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. This field is optional; omitted or `null` means there is no\nchunk-level metadata.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. This field is chunk-scoped.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -3895,7 +3895,7 @@ "required": ["configOptions"] }, "SessionInfoUpdate": { - "description": "Update to session metadata. All fields are optional to support partial updates.\n\nAgents send this notification to update session information like title or custom metadata.\nThis allows clients to display dynamic session names and track session state changes.", + "description": "Update to session metadata. All fields are optional to support partial updates.\n\nAgents send this notification to update session information like title or custom metadata.\nThis allows clients to display dynamic session names and track session state changes.\n\nOmitted fields leave the existing session info unchanged. `null` clears the\ncorresponding value.", "type": "object", "properties": { "title": { @@ -3909,7 +3909,7 @@ "x-deserialize-default-on-error": true }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted means no metadata update; `null` is an\nexplicit clear signal. Implementations MUST NOT make assumptions about values at these keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true diff --git a/schema/v2/schema.unstable.json b/schema/v2/schema.unstable.json index db451dcdd..e79c71135 100644 --- a/schema/v2/schema.unstable.json +++ b/schema/v2/schema.unstable.json @@ -789,7 +789,7 @@ "x-deserialize-default-on-error": true }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted means no metadata update; `null` is an\nexplicit clear signal. Implementations MUST NOT make assumptions about values at these keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -5773,7 +5773,7 @@ "type": "string" }, "ContentChunk": { - "description": "A streamed item of content", + "description": "A streamed item of message content.", "type": "object", "properties": { "messageId": { @@ -5793,7 +5793,7 @@ ] }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. This field is chunk-scoped.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -5802,7 +5802,7 @@ "required": ["messageId", "content"] }, "UserMessage": { - "description": "A user message upsert.\n\nOnly [`UserMessage::message_id`] is required. Other fields have patch\nsemantics: omitted fields leave the existing message value unchanged, `null`\nclears or unsets the value, and concrete values replace the previous value.\nFor a new `messageId`, omitted fields use client defaults. `content` is\nreplaced as a whole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\na `user_message` update includes `content`, that array replaces any content\npreviously accumulated for the message, including content from earlier\nchunks. Later chunks with the same `messageId` append to the current\ncontent.", + "description": "A user message upsert.\n\nOnly [`UserMessage::message_id`] is required. `content` has patch semantics:\nan omitted field leaves existing message content unchanged, `null` clears the\nvalue, and a concrete array replaces the previous value. For a new\n`messageId`, omitted fields use client defaults. `content` is replaced as a\nwhole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\na `user_message` update includes `content`, that array replaces any content\npreviously accumulated for the message, including content from earlier\nchunks. Later chunks with the same `messageId` append to the current\ncontent.", "type": "object", "properties": { "messageId": { @@ -5823,7 +5823,7 @@ "x-deserialize-skip-invalid-items": true }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. Omitted means no metadata update; `null` is an explicit clear signal.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -5832,7 +5832,7 @@ "required": ["messageId"] }, "AgentMessage": { - "description": "An agent message upsert.\n\nOnly [`AgentMessage::message_id`] is required. Other fields have patch\nsemantics: omitted fields leave the existing message value unchanged, `null`\nclears or unsets the value, and concrete values replace the previous value.\nFor a new `messageId`, omitted fields use client defaults. `content` is\nreplaced as a whole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\nan `agent_message` update includes `content`, that array replaces any\ncontent previously accumulated for the message, including content from\nearlier chunks. Later chunks with the same `messageId` append to the current\ncontent.", + "description": "An agent message upsert.\n\nOnly [`AgentMessage::message_id`] is required. `content` has patch semantics:\nan omitted field leaves existing message content unchanged, `null` clears the\nvalue, and a concrete array replaces the previous value. For a new\n`messageId`, omitted fields use client defaults. `content` is replaced as a\nwhole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\nan `agent_message` update includes `content`, that array replaces any\ncontent previously accumulated for the message, including content from\nearlier chunks. Later chunks with the same `messageId` append to the current\ncontent.", "type": "object", "properties": { "messageId": { @@ -5853,7 +5853,7 @@ "x-deserialize-skip-invalid-items": true }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. Omitted means no metadata update; `null` is an explicit clear signal.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -5862,7 +5862,7 @@ "required": ["messageId"] }, "AgentThought": { - "description": "An agent thought or reasoning message upsert.\n\nOnly [`AgentThought::message_id`] is required. Other fields have patch\nsemantics: omitted fields leave the existing thought value unchanged, `null`\nclears or unsets the value, and concrete values replace the previous value.\nFor a new `messageId`, omitted fields use client defaults. `content` is\nreplaced as a whole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\nan `agent_thought` update includes `content`, that array replaces any\ncontent previously accumulated for the thought, including content from\nearlier chunks. Later chunks with the same `messageId` append to the current\ncontent.", + "description": "An agent thought or reasoning message upsert.\n\nOnly [`AgentThought::message_id`] is required. `content` has patch semantics:\nan omitted field leaves existing thought content unchanged, `null` clears the\nvalue, and a concrete array replaces the previous value. For a new\n`messageId`, omitted fields use client defaults. `content` is replaced as a\nwhole array; send `[]` or `null` to clear it.\n\nMessage updates and chunks are applied in the order they are received. When\nan `agent_thought` update includes `content`, that array replaces any\ncontent previously accumulated for the thought, including content from\nearlier chunks. Later chunks with the same `messageId` append to the current\ncontent.", "type": "object", "properties": { "messageId": { @@ -5883,7 +5883,7 @@ "x-deserialize-skip-invalid-items": true }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. Omitted means no metadata update; `null` is an explicit clear signal.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -6162,7 +6162,7 @@ ] }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. This field is optional; omitted or `null` means there is no\nchunk-level metadata.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys. This field is chunk-scoped.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true @@ -6636,7 +6636,7 @@ "required": ["configOptions"] }, "SessionInfoUpdate": { - "description": "Update to session metadata. All fields are optional to support partial updates.\n\nAgents send this notification to update session information like title or custom metadata.\nThis allows clients to display dynamic session names and track session state changes.", + "description": "Update to session metadata. All fields are optional to support partial updates.\n\nAgents send this notification to update session information like title or custom metadata.\nThis allows clients to display dynamic session names and track session state changes.\n\nOmitted fields leave the existing session info unchanged. `null` clears the\ncorresponding value.", "type": "object", "properties": { "title": { @@ -6650,7 +6650,7 @@ "x-deserialize-default-on-error": true }, "_meta": { - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted means no metadata update; `null` is an\nexplicit clear signal. Implementations MUST NOT make assumptions about values at these keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", "type": ["object", "null"], "x-deserialize-default-on-error": true, "additionalProperties": true