From 132a6010e370e0f6b21ebe32b8990225a39c6ab5 Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Fri, 12 Dec 2025 22:27:23 +0100 Subject: [PATCH 1/3] feat: add SessionInfoUpdate to SessionUpdate enum Add a new session_info_update variant to the SessionUpdate discriminated union, allowing agents to notify clients about session metadata changes (title, timestamps, custom metadata) in real-time. --- docs/protocol/draft/schema.mdx | 51 ++++++++++++++++++++++++++++++ docs/protocol/schema.mdx | 51 ++++++++++++++++++++++++++++++ schema/schema.json | 35 +++++++++++++++++++++ schema/schema.unstable.json | 35 +++++++++++++++++++++ src/client.rs | 57 ++++++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+) diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index de6839d..b3d4195 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -2926,6 +2926,32 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte ISO 8601 timestamp of last activity +## SessionInfoUpdate + +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. + +**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. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + + + + Human-readable title for the session. Set to null to clear. + + + ISO 8601 timestamp of last activity. Set to null to clear. + + ## SessionListCapabilities Capabilities for the `session/list` method. @@ -3288,6 +3314,31 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte + +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. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + + + + + + Human-readable title for the session. Set to null to clear. + + + ISO 8601 timestamp of last activity. Set to null to clear. + + + + + ## StopReason Reasons why an agent stops processing a prompt turn. diff --git a/docs/protocol/schema.mdx b/docs/protocol/schema.mdx index 9f8143a..c6b88c6 100644 --- a/docs/protocol/schema.mdx +++ b/docs/protocol/schema.mdx @@ -2421,6 +2421,32 @@ See protocol docs: [Session ID](https://agentclientprotocol.com/protocol/session **Type:** `string` +## SessionInfoUpdate + +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. + +**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. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + + + + Human-readable title for the session. Set to null to clear. + + + ISO 8601 timestamp of last activity. Set to null to clear. + + ## SessionMode A mode the agent can operate in. @@ -2712,6 +2738,31 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte + +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. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + + + + + + Human-readable title for the session. Set to null to clear. + + + ISO 8601 timestamp of last activity. Set to null to clear. + + + + + ## StopReason Reasons why an agent stops processing a prompt turn. diff --git a/schema/schema.json b/schema/schema.json index 560732a..8ef737c 100644 --- a/schema/schema.json +++ b/schema/schema.json @@ -2067,6 +2067,25 @@ "description": "A unique identifier for a conversation session between a client and agent.\n\nSessions maintain their own context, conversation history, and state,\nallowing multiple independent interactions with the same agent.\n\nSee protocol docs: [Session ID](https://agentclientprotocol.com/protocol/session-setup#session-id)", "type": "string" }, + "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.", + "properties": { + "_meta": { + "additionalProperties": true, + "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/extensibility)", + "type": ["object", "null"] + }, + "title": { + "description": "Human-readable title for the session. Set to null to clear.", + "type": ["string", "null"] + }, + "updatedAt": { + "description": "ISO 8601 timestamp of last activity. Set to null to clear.", + "type": ["string", "null"] + } + }, + "type": "object" + }, "SessionMode": { "description": "A mode the agent can operate in.\n\nSee protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)", "properties": { @@ -2282,6 +2301,22 @@ }, "required": ["sessionUpdate"], "type": "object" + }, + { + "allOf": [ + { + "$ref": "#/$defs/SessionInfoUpdate" + } + ], + "description": "Session metadata has been updated (title, timestamps, custom metadata)", + "properties": { + "sessionUpdate": { + "const": "session_info_update", + "type": "string" + } + }, + "required": ["sessionUpdate"], + "type": "object" } ] }, diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index 0c97d63..3acf713 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -2460,6 +2460,25 @@ "required": ["sessionId", "cwd"], "type": "object" }, + "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.", + "properties": { + "_meta": { + "additionalProperties": true, + "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/extensibility)", + "type": ["object", "null"] + }, + "title": { + "description": "Human-readable title for the session. Set to null to clear.", + "type": ["string", "null"] + }, + "updatedAt": { + "description": "ISO 8601 timestamp of last activity. Set to null to clear.", + "type": ["string", "null"] + } + }, + "type": "object" + }, "SessionListCapabilities": { "description": "Capabilities for the `session/list` method.\n\nBy supplying `{}` it means that the agent supports listing of sessions.\n\nFurther capabilities can be added in the future for other means of filtering or searching the list.", "properties": { @@ -2724,6 +2743,22 @@ }, "required": ["sessionUpdate"], "type": "object" + }, + { + "allOf": [ + { + "$ref": "#/$defs/SessionInfoUpdate" + } + ], + "description": "Session metadata has been updated (title, timestamps, custom metadata)", + "properties": { + "sessionUpdate": { + "const": "session_info_update", + "type": "string" + } + }, + "required": ["sessionUpdate"], + "type": "object" } ] }, diff --git a/src/client.rs b/src/client.rs index bcfdd03..d2b6b95 100644 --- a/src/client.rs +++ b/src/client.rs @@ -90,6 +90,8 @@ pub enum SessionUpdate { /// /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes) CurrentModeUpdate(CurrentModeUpdate), + /// Session metadata has been updated (title, timestamps, custom metadata) + SessionInfoUpdate(SessionInfoUpdate), } /// The current mode of the session has changed @@ -131,6 +133,61 @@ impl CurrentModeUpdate { } } +/// 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. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct SessionInfoUpdate { + /// Human-readable title for the session. Set to null to clear. + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + /// ISO 8601 timestamp of last activity. Set to null to clear. + #[serde(skip_serializing_if = "Option::is_none")] + pub updated_at: Option, + /// 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. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl SessionInfoUpdate { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Human-readable title for the session. Set to null to clear. + #[must_use] + pub fn title(mut self, title: impl IntoOption) -> Self { + self.title = title.into_option(); + self + } + + /// ISO 8601 timestamp of last activity. Set to null to clear. + #[must_use] + pub fn updated_at(mut self, updated_at: impl IntoOption) -> Self { + self.updated_at = updated_at.into_option(); + self + } + + /// 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. + /// + /// 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(); + self + } +} + /// A streamed item of content #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] #[serde(rename_all = "camelCase")] From 3525800f514c8d7f02f24ed62919636c481c6ecd Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Mon, 15 Dec 2025 12:01:12 +0100 Subject: [PATCH 2/3] Put behind feature flag --- Cargo.toml | 14 ++++++++--- docs/protocol/schema.mdx | 51 ---------------------------------------- schema/schema.json | 35 --------------------------- src/client.rs | 3 +++ 4 files changed, 14 insertions(+), 89 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cd69068..70dd25c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,11 +14,19 @@ categories = ["development-tools", "api-bindings"] include = ["/src/**/*.rs", "/README.md", "/LICENSE", "/Cargo.toml"] [features] -unstable = ["unstable_session_model", "unstable_session_list", "unstable_session_fork", "unstable_session_resume", "unstable_cancel_request"] +unstable = [ + "unstable_cancel_request", + "unstable_session_fork", + "unstable_session_info_update", + "unstable_session_list", + "unstable_session_model", + "unstable_session_resume", +] unstable_cancel_request = [] -unstable_session_model = [] -unstable_session_list = [] unstable_session_fork = [] +unstable_session_info_update = [] +unstable_session_list = [] +unstable_session_model = [] unstable_session_resume = [] [[bin]] diff --git a/docs/protocol/schema.mdx b/docs/protocol/schema.mdx index c6b88c6..9f8143a 100644 --- a/docs/protocol/schema.mdx +++ b/docs/protocol/schema.mdx @@ -2421,32 +2421,6 @@ See protocol docs: [Session ID](https://agentclientprotocol.com/protocol/session **Type:** `string` -## SessionInfoUpdate - -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. - -**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. - -See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) - - - - Human-readable title for the session. Set to null to clear. - - - ISO 8601 timestamp of last activity. Set to null to clear. - - ## SessionMode A mode the agent can operate in. @@ -2738,31 +2712,6 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte - -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. - -See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) - - - - - - Human-readable title for the session. Set to null to clear. - - - ISO 8601 timestamp of last activity. Set to null to clear. - - - - - ## StopReason Reasons why an agent stops processing a prompt turn. diff --git a/schema/schema.json b/schema/schema.json index 8ef737c..560732a 100644 --- a/schema/schema.json +++ b/schema/schema.json @@ -2067,25 +2067,6 @@ "description": "A unique identifier for a conversation session between a client and agent.\n\nSessions maintain their own context, conversation history, and state,\nallowing multiple independent interactions with the same agent.\n\nSee protocol docs: [Session ID](https://agentclientprotocol.com/protocol/session-setup#session-id)", "type": "string" }, - "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.", - "properties": { - "_meta": { - "additionalProperties": true, - "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/extensibility)", - "type": ["object", "null"] - }, - "title": { - "description": "Human-readable title for the session. Set to null to clear.", - "type": ["string", "null"] - }, - "updatedAt": { - "description": "ISO 8601 timestamp of last activity. Set to null to clear.", - "type": ["string", "null"] - } - }, - "type": "object" - }, "SessionMode": { "description": "A mode the agent can operate in.\n\nSee protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)", "properties": { @@ -2301,22 +2282,6 @@ }, "required": ["sessionUpdate"], "type": "object" - }, - { - "allOf": [ - { - "$ref": "#/$defs/SessionInfoUpdate" - } - ], - "description": "Session metadata has been updated (title, timestamps, custom metadata)", - "properties": { - "sessionUpdate": { - "const": "session_info_update", - "type": "string" - } - }, - "required": ["sessionUpdate"], - "type": "object" } ] }, diff --git a/src/client.rs b/src/client.rs index d2b6b95..569b31b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -90,6 +90,7 @@ pub enum SessionUpdate { /// /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes) CurrentModeUpdate(CurrentModeUpdate), + #[cfg(feature = "unstable_session_info_update")] /// Session metadata has been updated (title, timestamps, custom metadata) SessionInfoUpdate(SessionInfoUpdate), } @@ -137,6 +138,7 @@ impl CurrentModeUpdate { /// /// 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. +#[cfg(feature = "unstable_session_info_update")] #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] #[non_exhaustive] @@ -156,6 +158,7 @@ pub struct SessionInfoUpdate { pub meta: Option, } +#[cfg(feature = "unstable_session_info_update")] impl SessionInfoUpdate { #[must_use] pub fn new() -> Self { From ee373d350144053d8ff4e4fd5956a04b7f6289a3 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Mon, 15 Dec 2025 13:00:58 +0100 Subject: [PATCH 3/3] Use maybeundefined --- src/client.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/src/client.rs b/src/client.rs index 569b31b..274c990 100644 --- a/src/client.rs +++ b/src/client.rs @@ -13,6 +13,8 @@ use crate::{ ContentBlock, ExtNotification, ExtRequest, ExtResponse, IntoOption, Meta, Plan, SessionId, SessionModeId, ToolCall, ToolCallUpdate, }; +#[cfg(feature = "unstable_session_info_update")] +use crate::{IntoMaybeUndefined, MaybeUndefined}; // Session updates @@ -144,11 +146,11 @@ impl CurrentModeUpdate { #[non_exhaustive] pub struct SessionInfoUpdate { /// Human-readable title for the session. Set to null to clear. - #[serde(skip_serializing_if = "Option::is_none")] - pub title: Option, + #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")] + pub title: MaybeUndefined, /// ISO 8601 timestamp of last activity. Set to null to clear. - #[serde(skip_serializing_if = "Option::is_none")] - pub updated_at: Option, + #[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. @@ -167,15 +169,15 @@ impl SessionInfoUpdate { /// Human-readable title for the session. Set to null to clear. #[must_use] - pub fn title(mut self, title: impl IntoOption) -> Self { - self.title = title.into_option(); + pub fn title(mut self, title: impl IntoMaybeUndefined) -> Self { + self.title = title.into_maybe_undefined(); self } /// ISO 8601 timestamp of last activity. Set to null to clear. #[must_use] - pub fn updated_at(mut self, updated_at: impl IntoOption) -> Self { - self.updated_at = updated_at.into_option(); + pub fn updated_at(mut self, updated_at: impl IntoMaybeUndefined) -> Self { + self.updated_at = updated_at.into_maybe_undefined(); self } @@ -1647,3 +1649,65 @@ impl AgentNotification { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "unstable_session_info_update")] + #[test] + fn test_serialization_behavior() { + use serde_json::json; + + assert_eq!( + serde_json::from_value::(json!({})).unwrap(), + SessionInfoUpdate { + title: MaybeUndefined::Undefined, + updated_at: MaybeUndefined::Undefined, + meta: None + } + ); + assert_eq!( + serde_json::from_value::(json!({"title": null, "updatedAt": null})) + .unwrap(), + SessionInfoUpdate { + title: MaybeUndefined::Null, + updated_at: MaybeUndefined::Null, + meta: None + } + ); + assert_eq!( + serde_json::from_value::( + json!({"title": "title", "updatedAt": "timestamp"}) + ) + .unwrap(), + SessionInfoUpdate { + title: MaybeUndefined::Value("title".to_string()), + updated_at: MaybeUndefined::Value("timestamp".to_string()), + meta: None + } + ); + + assert_eq!( + serde_json::to_value(SessionInfoUpdate::new()).unwrap(), + json!({}) + ); + assert_eq!( + serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(), + json!({"title": "title"}) + ); + assert_eq!( + serde_json::to_value(SessionInfoUpdate::new().title(None)).unwrap(), + json!({"title": null}) + ); + assert_eq!( + serde_json::to_value( + SessionInfoUpdate::new() + .title("title") + .title(MaybeUndefined::Undefined) + ) + .unwrap(), + json!({}) + ); + } +}