diff --git a/agent-client-protocol-schema/src/v2/agent.rs b/agent-client-protocol-schema/src/v2/agent.rs index 08efb0e15..c565cb030 100644 --- a/agent-client-protocol-schema/src/v2/agent.rs +++ b/agent-client-protocol-schema/src/v2/agent.rs @@ -1214,28 +1214,35 @@ impl NewSessionResponse { } } -// Load session +// Fork session -/// Request parameters for loading an existing session. +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Request parameters for forking an existing session. +/// +/// Creates a new session based on the context of an existing one, allowing +/// operations like generating summaries without affecting the original session's history. /// -/// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions) +/// Only available if the Agent supports the `session.fork` capability. +#[cfg(feature = "unstable_session_fork")] #[serde_as] #[skip_serializing_none] #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME))] +#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct LoadSessionRequest { - /// The ID of the session to load. +pub struct ForkSessionRequest { + /// The ID of the session to fork. pub session_id: SessionId, /// The working directory for this session. Must be an absolute path. pub cwd: PathBuf, /// Additional workspace roots to activate for this session. Each path must be absolute. /// /// When omitted or empty, no additional roots are activated. When non-empty, - /// this is the complete resulting additional-root list for the loaded - /// session. It may differ from any previously used or reported list as long as - /// the request `cwd` matches the session's `cwd`. + /// this is the complete resulting additional-root list for the forked + /// session. #[serde_as(deserialize_as = "DefaultOnError>")] #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] #[serde(default, skip_serializing_if = "Vec::is_empty")] @@ -1257,15 +1264,16 @@ pub struct LoadSessionRequest { pub meta: Option, } -impl LoadSessionRequest { - /// Builds [`LoadSessionRequest`] with the required request fields set; optional fields start unset or empty. +#[cfg(feature = "unstable_session_fork")] +impl ForkSessionRequest { + /// Builds [`ForkSessionRequest`] with the required request fields set; optional fields start unset or empty. #[must_use] pub fn new(session_id: impl Into, cwd: impl Into) -> Self { Self { - mcp_servers: vec![], + session_id: session_id.into(), cwd: cwd.into(), additional_directories: vec![], - session_id: session_id.into(), + mcp_servers: vec![], meta: None, } } @@ -1296,14 +1304,21 @@ impl LoadSessionRequest { } } -/// Response from loading an existing session. +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Response from forking an existing session. +#[cfg(feature = "unstable_session_fork")] #[serde_as] #[skip_serializing_none] -#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME))] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct LoadSessionResponse { +pub struct ForkSessionResponse { + /// Unique identifier for the newly created forked session. + pub session_id: SessionId, /// Initial session configuration options. #[serde_as(deserialize_as = "DefaultOnError>")] #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] @@ -1321,11 +1336,16 @@ pub struct LoadSessionResponse { pub meta: Option, } -impl LoadSessionResponse { - /// Builds [`LoadSessionResponse`] with the required response fields set; optional fields start unset or empty. +#[cfg(feature = "unstable_session_fork")] +impl ForkSessionResponse { + /// Builds [`ForkSessionResponse`] with the required response fields set; optional fields start unset or empty. #[must_use] - pub fn new() -> Self { - Self::default() + pub fn new(session_id: impl Into) -> Self { + Self { + session_id: session_id.into(), + config_options: Vec::new(), + meta: None, + } } /// Initial session configuration options. @@ -1347,35 +1367,29 @@ impl LoadSessionResponse { } } -// Fork session +// Resume session -/// **UNSTABLE** -/// -/// This capability is not part of the spec yet, and may be removed or changed at any point. -/// -/// Request parameters for forking an existing session. -/// -/// Creates a new session based on the context of an existing one, allowing -/// operations like generating summaries without affecting the original session's history. +/// Request parameters for resuming an existing session. /// -/// Only available if the Agent supports the `session.fork` capability. -#[cfg(feature = "unstable_session_fork")] +/// Resumes an existing session and optionally replays prior conversation +/// history according to `replayFrom`. #[serde_as] #[skip_serializing_none] #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))] +#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct ForkSessionRequest { - /// The ID of the session to fork. +pub struct ResumeSessionRequest { + /// The ID of the session to resume. pub session_id: SessionId, /// The working directory for this session. Must be an absolute path. pub cwd: PathBuf, /// Additional workspace roots to activate for this session. Each path must be absolute. /// /// When omitted or empty, no additional roots are activated. When non-empty, - /// this is the complete resulting additional-root list for the forked - /// session. + /// this is the complete resulting additional-root list for the resumed + /// session. It may differ from any previously used or reported list as long as + /// the request `cwd` matches the session's `cwd`. #[serde_as(deserialize_as = "DefaultOnError>")] #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] #[serde(default, skip_serializing_if = "Vec::is_empty")] @@ -1385,6 +1399,17 @@ pub struct ForkSessionRequest { #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] #[serde(default, skip_serializing_if = "Vec::is_empty")] pub mcp_servers: Vec, + /// Inclusive cursor describing where conversation replay should begin. + /// + /// Optional. Omitted or `null` both mean the Agent should resume without + /// replaying previous conversation history. Replay cursors are inclusive: + /// replay includes the position identified by the cursor. Supplying + /// `{ "type": "start" }` means the Agent should replay the whole + /// conversation before responding. + #[serde_as(deserialize_as = "DefaultOnError")] + #[schemars(extend("x-deserialize-default-on-error" = true))] + #[serde(default)] + pub replay_from: 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. @@ -1397,9 +1422,8 @@ pub struct ForkSessionRequest { pub meta: Option, } -#[cfg(feature = "unstable_session_fork")] -impl ForkSessionRequest { - /// Builds [`ForkSessionRequest`] with the required request fields set; optional fields start unset or empty. +impl ResumeSessionRequest { + /// Builds [`ResumeSessionRequest`] with the required request fields set; optional fields start unset or empty. #[must_use] pub fn new(session_id: impl Into, cwd: impl Into) -> Self { Self { @@ -1407,6 +1431,7 @@ impl ForkSessionRequest { cwd: cwd.into(), additional_directories: vec![], mcp_servers: vec![], + replay_from: None, meta: None, } } @@ -1425,6 +1450,19 @@ impl ForkSessionRequest { self } + /// Inclusive cursor describing where conversation replay should begin. + /// + /// Omitted or `null` both mean the Agent should resume without replaying + /// previous conversation history. Replay cursors are inclusive: replay + /// includes the position identified by the cursor. Supplying + /// `{ "type": "start" }` means the Agent should replay the whole + /// conversation before responding. + #[must_use] + pub fn replay_from(mut self, replay_from: impl IntoOption) -> Self { + self.replay_from = replay_from.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. @@ -1437,26 +1475,42 @@ impl ForkSessionRequest { } } -/// **UNSTABLE** -/// -/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// Inclusive cursor describing where replayed session history should begin. /// -/// Response from forking an existing session. -#[cfg(feature = "unstable_session_fork")] +/// Replay includes the position identified by the cursor. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(tag = "type", rename_all = "snake_case")] +#[schemars(extend("discriminator" = {"propertyName": "type"}))] +#[non_exhaustive] +pub enum ReplayFrom { + /// Replay the whole conversation from its first replayable entry. + Start(ReplayFromStart), + /// Custom or future replay cursor. + /// + /// Values beginning with `_` are reserved for implementation-specific + /// extensions. Unknown values that do not begin with `_` are reserved for + /// future ACP variants. + /// + /// Receivers that do not understand this cursor should preserve the raw + /// payload when storing, replaying, proxying, or forwarding requests, and + /// otherwise reject the request rather than guessing where to replay from. + #[serde(untagged)] + Other(OtherReplayFrom), +} + +impl From for ReplayFrom { + fn from(replay_from: ReplayFromStart) -> Self { + Self::Start(replay_from) + } +} + +/// Inclusive replay cursor requesting replay from the start of the conversation. #[serde_as] #[skip_serializing_none] -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME))] +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct ForkSessionResponse { - /// Unique identifier for the newly created forked session. - pub session_id: SessionId, - /// Initial session configuration options. - #[serde_as(deserialize_as = "DefaultOnError>")] - #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub config_options: Vec, +pub struct ReplayFromStart { /// 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. @@ -1469,23 +1523,11 @@ pub struct ForkSessionResponse { pub meta: Option, } -#[cfg(feature = "unstable_session_fork")] -impl ForkSessionResponse { - /// Builds [`ForkSessionResponse`] with the required response fields set; optional fields start unset or empty. +impl ReplayFromStart { + /// Builds [`ReplayFromStart`]. #[must_use] - pub fn new(session_id: impl Into) -> Self { - Self { - session_id: session_id.into(), - config_options: Vec::new(), - meta: None, - } - } - - /// Initial session configuration options. - #[must_use] - pub fn config_options(mut self, config_options: Vec) -> Self { - self.config_options = config_options; - self + pub fn new() -> Self { + Self::default() } /// The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -1500,39 +1542,22 @@ impl ForkSessionResponse { } } -// Resume session - -/// Request parameters for resuming an existing session. -/// -/// Resumes an existing session without returning previous messages (unlike `session/load`). -/// This is useful when a Client already has the session history and only needs -/// to reconnect to the Agent's session state. +/// Custom or future replay cursor payload. #[serde_as] #[skip_serializing_none] -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME))] +#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)] +#[schemars(inline)] +#[schemars(transform = other_replay_from_schema)] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct ResumeSessionRequest { - /// The ID of the session to resume. - pub session_id: SessionId, - /// The working directory for this session. Must be an absolute path. - pub cwd: PathBuf, - /// Additional workspace roots to activate for this session. Each path must be absolute. +pub struct OtherReplayFrom { + /// Custom or future replay cursor type. /// - /// When omitted or empty, no additional roots are activated. When non-empty, - /// this is the complete resulting additional-root list for the resumed - /// session. It may differ from any previously used or reported list as long as - /// the request `cwd` matches the session's `cwd`. - #[serde_as(deserialize_as = "DefaultOnError>")] - #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub additional_directories: Vec, - /// List of MCP servers to connect to for this session. - #[serde_as(deserialize_as = "DefaultOnError>")] - #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub mcp_servers: Vec, + /// Values beginning with `_` are reserved for implementation-specific + /// extensions. Unknown values that do not begin with `_` are reserved for + /// future ACP variants. + #[serde(rename = "type")] + pub type_: String, /// 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. @@ -1543,35 +1568,24 @@ pub struct ResumeSessionRequest { #[serde(default)] #[serde(rename = "_meta")] pub meta: Option, + /// Additional fields from the unknown replay cursor payload. + #[serde(flatten)] + pub fields: BTreeMap, } -impl ResumeSessionRequest { - /// Builds [`ResumeSessionRequest`] with the required request fields set; optional fields start unset or empty. +impl OtherReplayFrom { + /// Builds [`OtherReplayFrom`] from an unknown discriminator and preserves the remaining extension fields. #[must_use] - pub fn new(session_id: impl Into, cwd: impl Into) -> Self { + pub fn new(type_: impl Into, mut fields: BTreeMap) -> Self { + fields.remove("type"); + fields.remove("_meta"); Self { - session_id: session_id.into(), - cwd: cwd.into(), - additional_directories: vec![], - mcp_servers: vec![], + type_: type_.into(), meta: None, + fields, } } - /// Additional workspace roots to activate for this session. Each path must be absolute. - #[must_use] - pub fn additional_directories(mut self, additional_directories: Vec) -> Self { - self.additional_directories = additional_directories; - self - } - - /// List of MCP servers to connect to for this session. - #[must_use] - pub fn mcp_servers(mut self, mcp_servers: Vec) -> Self { - self.mcp_servers = mcp_servers; - 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. @@ -1584,6 +1598,45 @@ impl ResumeSessionRequest { } } +impl<'de> Deserialize<'de> for OtherReplayFrom { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let mut fields = BTreeMap::::deserialize(deserializer)?; + let type_ = fields + .remove("type") + .ok_or_else(|| serde::de::Error::missing_field("type"))?; + let serde_json::Value::String(type_) = type_ else { + return Err(serde::de::Error::custom("`type` must be a string")); + }; + + if is_known_replay_from_type(&type_) { + return Err(serde::de::Error::custom(format!( + "known replay cursor `{type_}` did not match its schema" + ))); + } + + let meta = fields + .remove("_meta") + .and_then(|value| serde_json::from_value(value).ok()); + + Ok(Self { + type_, + meta, + fields, + }) + } +} + +fn is_known_replay_from_type(type_: &str) -> bool { + matches!(type_, "start") +} + +fn other_replay_from_schema(schema: &mut Schema) { + super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]); +} + /// Response from resuming an existing session. #[serde_as] #[skip_serializing_none] @@ -4216,9 +4269,8 @@ impl ProvidersCapabilities { /// Session capabilities supported by the agent. /// /// Supplying `{}` means the agent supports the baseline session methods: -/// `session/new`, `session/load`, `session/list`, `session/resume`, -/// `session/close`, `session/prompt`, `session/cancel`, and -/// `session/update`. +/// `session/new`, `session/list`, `session/resume`, `session/close`, +/// `session/prompt`, `session/cancel`, and `session/update`. /// /// Agents that support sessions **MAY** support additional session methods, /// prompt content types, and MCP transports by specifying additional @@ -5020,8 +5072,6 @@ pub struct AgentMethodNames { pub providers_disable: &'static str, /// Method for creating a new session. pub session_new: &'static str, - /// Method for loading an existing session. - pub session_load: &'static str, /// Method for setting a configuration option for a session. pub session_set_config_option: &'static str, /// Method for sending a prompt to the agent. @@ -5087,7 +5137,6 @@ pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames { #[cfg(feature = "unstable_llm_providers")] providers_disable: PROVIDERS_DISABLE_METHOD_NAME, session_new: SESSION_NEW_METHOD_NAME, - session_load: SESSION_LOAD_METHOD_NAME, session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME, session_prompt: SESSION_PROMPT_METHOD_NAME, session_cancel: SESSION_CANCEL_METHOD_NAME, @@ -5137,8 +5186,6 @@ pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set"; pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable"; /// Method name for creating a new session. pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new"; -/// Method name for loading an existing session. -pub(crate) const SESSION_LOAD_METHOD_NAME: &str = "session/load"; /// Method name for setting a configuration option for a session. pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option"; /// Method name for sending a prompt. @@ -5230,15 +5277,6 @@ pub enum ClientRequest { /// /// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/session-setup) NewSessionRequest(Box), - /// Loads an existing session to resume a previous conversation. - /// - /// The agent should: - /// - Restore the session context and conversation history - /// - Connect to the specified MCP servers - /// - Stream the entire conversation history back to the client via notifications - /// - /// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions) - LoadSessionRequest(Box), /// Lists existing sessions known to the agent. /// /// The agent should return metadata about sessions with optional filtering and pagination support. @@ -5260,10 +5298,11 @@ pub enum ClientRequest { /// original, allowing operations like generating summaries without affecting the /// original session's history. ForkSessionRequest(Box), - /// Resumes an existing session without returning previous messages. + /// Resumes an existing session. /// - /// The agent should resume the session context, allowing the conversation to continue - /// without replaying the message history (unlike `session/load`). + /// The agent should resume the session context, allowing the conversation + /// to continue. If `replayFrom` is set, the agent should replay + /// conversation history before responding. ResumeSessionRequest(Box), /// Closes an active session and frees up any resources associated with it. /// @@ -5339,7 +5378,6 @@ impl ClientRequest { Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable, Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout, Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new, - Self::LoadSessionRequest(_) => AGENT_METHOD_NAMES.session_load, Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list, Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete, #[cfg(feature = "unstable_session_fork")] @@ -5389,8 +5427,6 @@ pub enum AgentResponse { LogoutAuthResponse(#[serde(default)] Box), /// Successful result returned for a `session/new` request. NewSessionResponse(Box), - /// Successful result returned for a `session/load` request. - LoadSessionResponse(#[serde(default)] Box), /// Successful result returned for a `session/list` request. ListSessionsResponse(Box), /// Successful result returned for a `session/delete` request. @@ -5902,10 +5938,6 @@ mod test_serialization { serde_json::to_value(NewSessionResponse::new("sess")).unwrap(), json!({ "sessionId": "sess" }) ); - assert_eq!( - serde_json::to_value(LoadSessionResponse::new()).unwrap(), - json!({}) - ); assert_eq!( serde_json::to_value(ResumeSessionResponse::new()).unwrap(), json!({}) @@ -5951,8 +5983,6 @@ mod test_serialization { .unwrap(); assert_eq!(mixed.config_options.len(), 1); - let load: LoadSessionResponse = serde_json::from_value(json!({})).unwrap(); - assert!(load.config_options.is_empty()); let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap(); assert!(resume.config_options.is_empty()); #[cfg(feature = "unstable_session_fork")] @@ -5963,6 +5993,53 @@ mod test_serialization { } } + #[test] + fn test_resume_session_replay_from_serialization() { + assert_eq!( + serde_json::to_value(ResumeSessionRequest::new( + "sess_abc123", + "/home/user/project" + )) + .unwrap(), + json!({ + "sessionId": "sess_abc123", + "cwd": "/home/user/project" + }) + ); + assert_eq!( + serde_json::to_value( + ResumeSessionRequest::new("sess_abc123", "/home/user/project") + .replay_from(ReplayFrom::from(ReplayFromStart::new())) + ) + .unwrap(), + json!({ + "sessionId": "sess_abc123", + "cwd": "/home/user/project", + "replayFrom": { + "type": "start" + } + }) + ); + + let replay: ResumeSessionRequest = serde_json::from_value(json!({ + "sessionId": "sess_abc123", + "cwd": "/home/user/project", + "replayFrom": { + "type": "start" + } + })) + .unwrap(); + assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_)))); + + let none: ResumeSessionRequest = serde_json::from_value(json!({ + "sessionId": "sess_abc123", + "cwd": "/home/user/project", + "replayFrom": null + })) + .unwrap(); + assert!(none.replay_from.is_none()); + } + #[test] fn test_auth_method_agent_serialization() { let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth")); @@ -6135,15 +6212,18 @@ mod test_serialization { }) ); assert_eq!( - serde_json::to_value(LoadSessionRequest::new("sess_abc123", "/home/user/project")) - .unwrap(), + serde_json::to_value(ResumeSessionRequest::new( + "sess_abc123", + "/home/user/project" + )) + .unwrap(), json!({ "sessionId": "sess_abc123", "cwd": "/home/user/project", }) ); assert_eq!( - serde_json::from_value::(json!({ + serde_json::from_value::(json!({ "sessionId": "sess_abc123", "cwd": "/home/user/project" })) @@ -6152,7 +6232,7 @@ mod test_serialization { Vec::::new() ); assert_eq!( - serde_json::from_value::(json!({ + serde_json::from_value::(json!({ "sessionId": "sess_abc123", "cwd": "/home/user/project", "mcpServers": null diff --git a/agent-client-protocol-schema/src/v2/conversion.rs b/agent-client-protocol-schema/src/v2/conversion.rs index 58832dc51..5dc8a02e6 100644 --- a/agent-client-protocol-schema/src/v2/conversion.rs +++ b/agent-client-protocol-schema/src/v2/conversion.rs @@ -3324,29 +3324,8 @@ impl IntoV2 for crate::v1::NewSessionResponse { } } -impl IntoV1 for super::LoadSessionRequest { - type Output = crate::v1::LoadSessionRequest; - - fn into_v1(self) -> Result { - let Self { - mcp_servers, - cwd, - additional_directories, - session_id, - meta, - } = self; - Ok(crate::v1::LoadSessionRequest { - mcp_servers: mcp_servers.into_v1()?, - cwd: cwd.into_v1()?, - additional_directories: additional_directories.into_v1()?, - session_id: session_id.into_v1()?, - meta: meta.into_v1()?, - }) - } -} - impl IntoV2 for crate::v1::LoadSessionRequest { - type Output = super::LoadSessionRequest; + type Output = super::ResumeSessionRequest; fn into_v2(self) -> Result { let Self { @@ -3356,34 +3335,19 @@ impl IntoV2 for crate::v1::LoadSessionRequest { session_id, meta, } = self; - Ok(super::LoadSessionRequest { + Ok(super::ResumeSessionRequest { mcp_servers: mcp_servers.into_v2()?, cwd: cwd.into_v2()?, additional_directories: additional_directories.into_v2()?, session_id: session_id.into_v2()?, + replay_from: Some(super::ReplayFrom::Start(super::ReplayFromStart::new())), meta: meta.into_v2()?, }) } } -impl IntoV1 for super::LoadSessionResponse { - type Output = crate::v1::LoadSessionResponse; - - fn into_v1(self) -> Result { - let Self { - config_options, - meta, - } = self; - Ok(crate::v1::LoadSessionResponse { - modes: None, - config_options: Some(into_v1_vec_skip_errors(config_options)), - meta: meta.into_v1()?, - }) - } -} - impl IntoV2 for crate::v1::LoadSessionResponse { - type Output = super::LoadSessionResponse; + type Output = super::ResumeSessionResponse; fn into_v2(self) -> Result { let Self { @@ -3391,13 +3355,60 @@ impl IntoV2 for crate::v1::LoadSessionResponse { config_options, meta, } = self; - Ok(super::LoadSessionResponse { + Ok(super::ResumeSessionResponse { config_options: option_vec_into_v2_default_skip_errors(config_options), meta: meta.into_v2()?, }) } } +fn v2_resume_session_request_into_v1_load( + request: super::ResumeSessionRequest, +) -> Result { + let super::ResumeSessionRequest { + session_id, + cwd, + additional_directories, + mcp_servers, + replay_from: _, + meta, + } = request; + Ok(crate::v1::LoadSessionRequest { + mcp_servers: mcp_servers.into_v1()?, + cwd: cwd.into_v1()?, + additional_directories: additional_directories.into_v1()?, + session_id: session_id.into_v1()?, + meta: meta.into_v1()?, + }) +} + +fn unsupported_replay_from_for_v1_resume( + replay_from: super::ReplayFrom, +) -> ProtocolConversionError { + match replay_from { + super::ReplayFrom::Start(_) => ProtocolConversionError::new( + "v2 ResumeSessionRequest `replayFrom: start` maps to v1 session/load, not v1 session/resume", + ), + super::ReplayFrom::Other(other) => unknown_v2_enum_variant("ReplayFrom", &other.type_), + } +} + +fn v2_resume_session_request_into_v1_client_request( + request: super::ResumeSessionRequest, +) -> Result { + match request.replay_from.clone() { + None => Ok(crate::v1::ClientRequest::ResumeSessionRequest( + request.into_v1()?, + )), + Some(super::ReplayFrom::Start(_)) => Ok(crate::v1::ClientRequest::LoadSessionRequest( + v2_resume_session_request_into_v1_load(request)?, + )), + Some(super::ReplayFrom::Other(other)) => { + Err(unknown_v2_enum_variant("ReplayFrom", &other.type_)) + } + } +} + #[cfg(feature = "unstable_session_fork")] impl IntoV1 for super::ForkSessionRequest { type Output = crate::v1::ForkSessionRequest; @@ -3489,8 +3500,12 @@ impl IntoV1 for super::ResumeSessionRequest { cwd, additional_directories, mcp_servers, + replay_from, meta, } = self; + if let Some(replay_from) = replay_from { + return Err(unsupported_replay_from_for_v1_resume(replay_from)); + } Ok(crate::v1::ResumeSessionRequest { session_id: session_id.into_v1()?, cwd: cwd.into_v1()?, @@ -3517,6 +3532,7 @@ impl IntoV2 for crate::v1::ResumeSessionRequest { cwd: cwd.into_v2()?, additional_directories: additional_directories.into_v2()?, mcp_servers: mcp_servers.into_v2()?, + replay_from: None, meta: meta.into_v2()?, }) } @@ -5199,9 +5215,6 @@ impl IntoV1 for super::ClientRequest { Self::NewSessionRequest(value) => { crate::v1::ClientRequest::NewSessionRequest(value.into_v1()?) } - Self::LoadSessionRequest(value) => { - crate::v1::ClientRequest::LoadSessionRequest(value.into_v1()?) - } Self::ListSessionsRequest(value) => { crate::v1::ClientRequest::ListSessionsRequest(value.into_v1()?) } @@ -5213,7 +5226,7 @@ impl IntoV1 for super::ClientRequest { crate::v1::ClientRequest::ForkSessionRequest(value.into_v1()?) } Self::ResumeSessionRequest(value) => { - crate::v1::ClientRequest::ResumeSessionRequest(value.into_v1()?) + v2_resume_session_request_into_v1_client_request(*value)? } Self::CloseSessionRequest(value) => { crate::v1::ClientRequest::CloseSessionRequest(value.into_v1()?) @@ -5275,7 +5288,7 @@ impl IntoV2 for crate::v1::ClientRequest { super::ClientRequest::NewSessionRequest(Box::new(value.into_v2()?)) } Self::LoadSessionRequest(value) => { - super::ClientRequest::LoadSessionRequest(Box::new(value.into_v2()?)) + super::ClientRequest::ResumeSessionRequest(Box::new(value.into_v2()?)) } Self::ListSessionsRequest(value) => { super::ClientRequest::ListSessionsRequest(Box::new(value.into_v2()?)) @@ -5354,9 +5367,6 @@ impl IntoV1 for super::AgentResponse { Self::NewSessionResponse(value) => { crate::v1::AgentResponse::NewSessionResponse(value.into_v1()?) } - Self::LoadSessionResponse(value) => { - crate::v1::AgentResponse::LoadSessionResponse(value.into_v1()?) - } Self::ListSessionsResponse(value) => { crate::v1::AgentResponse::ListSessionsResponse(value.into_v1()?) } @@ -5432,7 +5442,7 @@ impl IntoV2 for crate::v1::AgentResponse { super::AgentResponse::NewSessionResponse(Box::new(value.into_v2()?)) } Self::LoadSessionResponse(value) => { - super::AgentResponse::LoadSessionResponse(Box::new(value.into_v2()?)) + super::AgentResponse::ResumeSessionResponse(Box::new(value.into_v2()?)) } Self::ListSessionsResponse(value) => { super::AgentResponse::ListSessionsResponse(Box::new(value.into_v2()?)) @@ -10214,7 +10224,7 @@ mod tests { v1_to_v2(v1::NewSessionResponse::new("sess")).unwrap(); assert!(new_response.config_options.is_empty()); - let load_response: v2::LoadSessionResponse = + let load_response: v2::ResumeSessionResponse = v1_to_v2(v1::LoadSessionResponse::new()).unwrap(); assert!(load_response.config_options.is_empty()); @@ -10230,6 +10240,39 @@ mod tests { } } + #[test] + fn v2_resume_replay_from_start_maps_to_v1_load_request() { + let v1_load = v1::ClientRequest::LoadSessionRequest(v1::LoadSessionRequest::new( + "sess", + "/workspace/project", + )); + let v2_request: v2::ClientRequest = v1_to_v2(v1_load).unwrap(); + let v2::ClientRequest::ResumeSessionRequest(resume) = v2_request else { + panic!("v1 session/load should convert to v2 session/resume"); + }; + assert!(matches!(resume.replay_from, Some(v2::ReplayFrom::Start(_)))); + + let v1_request: v1::ClientRequest = + v2_to_v1(v2::ClientRequest::ResumeSessionRequest(resume)).unwrap(); + assert!(matches!( + v1_request, + v1::ClientRequest::LoadSessionRequest(_) + )); + } + + #[test] + fn v2_resume_without_replay_maps_to_v1_resume_request() { + let v2_request = v2::ClientRequest::ResumeSessionRequest(Box::new( + v2::ResumeSessionRequest::new("sess", "/workspace/project"), + )); + + let v1_request: v1::ClientRequest = v2_to_v1(v2_request).unwrap(); + assert!(matches!( + v1_request, + v1::ClientRequest::ResumeSessionRequest(_) + )); + } + #[test] fn v2_session_response_converts_to_v1_without_mode_state() { let response: v1::NewSessionResponse = diff --git a/docs/docs.json b/docs/docs.json index 4bb4cd662..78d37bc9d 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -206,6 +206,7 @@ "rfds/v2/prompt", "rfds/v2/enum-variant-extension", "rfds/v2/required-session-methods", + "rfds/v2/session-resume-replay", "rfds/v2/client-filesystem-terminal-capabilities", "rfds/v2/plan-variants", "rfds/v2/tool-call-updates", diff --git a/docs/protocol/v2/draft/initialization.mdx b/docs/protocol/v2/draft/initialization.mdx index fededccde..372d395fe 100644 --- a/docs/protocol/v2/draft/initialization.mdx +++ b/docs/protocol/v2/draft/initialization.mdx @@ -118,9 +118,9 @@ The Agent **SHOULD** specify whether it supports the following capabilities: The Agent supports the `session/*` method surface. Omitted or `null` means the Agent does not support session methods. Supplying `{}` means the Agent - supports the baseline session methods: `session/new`, `session/load`, - `session/list`, `session/resume`, `session/close`, `session/prompt`, - `session/cancel`, and `session/update`. + supports the baseline session methods: `session/new`, `session/list`, + `session/resume`, `session/close`, `session/prompt`, `session/cancel`, and + `session/update`. @@ -136,8 +136,8 @@ The Agent **SHOULD** specify whether it supports the following capabilities: #### Session Capabilities Supplying `session: {}` means the Agent supports `session/new`, -`session/load`, `session/list`, `session/resume`, `session/close`, -`session/prompt`, `session/cancel`, and `session/update`. +`session/list`, `session/resume`, `session/close`, `session/prompt`, +`session/cancel`, and `session/update`. Optionally, Agents **MAY** support prompt extensions, MCP server transports, and additional session methods such as `session/delete` by specifying nested diff --git a/docs/protocol/v2/draft/overview.mdx b/docs/protocol/v2/draft/overview.mdx index d19c878c2..718849176 100644 --- a/docs/protocol/v2/draft/overview.mdx +++ b/docs/protocol/v2/draft/overview.mdx @@ -31,7 +31,7 @@ A typical flow follows this pattern: - Client → Agent: `session/new` to create a new session -- Client → Agent: `session/load` or `session/resume` to resume an existing session +- Client → Agent: `session/resume` to resume an existing session @@ -90,13 +90,6 @@ Agents are programs that use generative AI to autonomously modify code. They typ Agent. -Schema]} -> - [Load an existing session](/protocol/v2/draft/session-setup#loading-sessions). - - Schema]} @@ -108,7 +101,7 @@ Agents are programs that use generative AI to autonomously modify code. They typ name="session/resume" post={[Schema]} > - [Resume an existing session without replaying + [Resume an existing session, optionally replaying history](/protocol/v2/draft/session-setup#resuming-sessions). diff --git a/docs/protocol/v2/draft/schema.mdx b/docs/protocol/v2/draft/schema.mdx index c07113cc5..9aa5bd870 100644 --- a/docs/protocol/v2/draft/schema.mdx +++ b/docs/protocol/v2/draft/schema.mdx @@ -1147,76 +1147,6 @@ to fetch the next page. If absent, there are no more results. Array of session information objects. - -### session/load - -Loads an existing session to resume a previous conversation. - -The agent should: - -- Restore the session context and conversation history -- Connect to the specified MCP servers -- Stream the entire conversation history back to the client via notifications - -See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/v2/draft/session-setup#loading-sessions) - -#### LoadSessionRequest - -Request parameters for loading an existing session. - -See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/v2/draft/session-setup#loading-sessions) - -**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/v2/draft/extensibility) - - -"string"[]} > - Additional workspace roots to activate for this session. Each path must be absolute. - -When omitted or empty, no additional roots are activated. When non-empty, -this is the complete resulting additional-root list for the loaded -session. It may differ from any previously used or reported list as long as -the request `cwd` matches the session's `cwd`. - - - - The working directory for this session. Must be an absolute path. - -McpServer[]} > - List of MCP servers to connect to for this session. - -SessionId} required> - The ID of the session to load. - - -#### LoadSessionResponse - -Response from loading an existing session. - -**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/v2/draft/extensibility) - - -SessionConfigOption[]} > - Initial session configuration options. - - ### session/new @@ -1376,18 +1306,18 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d ### session/resume -Resumes an existing session without returning previous messages. +Resumes an existing session. -The agent should resume the session context, allowing the conversation to continue -without replaying the message history (unlike `session/load`). +The agent should resume the session context, allowing the conversation +to continue. If `replayFrom` is set, the agent should replay +conversation history before responding. #### ResumeSessionRequest Request parameters for resuming an existing session. -Resumes an existing session without returning previous messages (unlike `session/load`). -This is useful when a Client already has the session history and only needs -to reconnect to the Agent's session state. +Resumes an existing session and optionally replays prior conversation +history according to `replayFrom`. **Type:** Object @@ -1415,6 +1345,16 @@ the request `cwd` matches the session's `cwd`. McpServer[]} > List of MCP servers to connect to for this session. + +ReplayFrom | null} > + Inclusive cursor describing where conversation replay should begin. + +Optional. Omitted or `null` both mean the Agent should resume without +replaying previous conversation history. Replay cursors are inclusive: +replay includes the position identified by the cursor. Supplying +`\{ "type": "start" \}` means the Agent should replay the whole +conversation before responding. + SessionId} required> The ID of the session to resume. @@ -6632,6 +6572,84 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d The start position (inclusive). +## ReplayFrom + +Inclusive cursor describing where replayed session history should begin. + +Replay includes the position identified by the cursor. + +**Type:** Union + + +Replay the whole conversation from its first replayable entry. + + + + + 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/v2/draft/extensibility) + + + + The discriminator value. Must be `"start"`. + + + + + + +Custom or future replay cursor. + +Values beginning with `_` are reserved for implementation-specific +extensions. Unknown values that do not begin with `_` are reserved for +future ACP variants. + +Receivers that do not understand this cursor should preserve the raw +payload when storing, replaying, proxying, or forwarding requests, and +otherwise reject the request rather than guessing where to replay from. + + + + + 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/v2/draft/extensibility) + + + + Custom or future replay cursor type. + +Values beginning with `_` are reserved for implementation-specific +extensions. Unknown values that do not begin with `_` are reserved for +future ACP variants. + + + + + + +## ReplayFromStart + +Inclusive replay cursor requesting replay from the start of the conversation. + +**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/v2/draft/extensibility) + + + ## RequestId JSON RPC Request Id @@ -6927,9 +6945,8 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Session capabilities supported by the agent. Supplying `\{\}` means the agent supports the baseline session methods: -`session/new`, `session/load`, `session/list`, `session/resume`, -`session/close`, `session/prompt`, `session/cancel`, and -`session/update`. +`session/new`, `session/list`, `session/resume`, `session/close`, +`session/prompt`, `session/cancel`, and `session/update`. Agents that support sessions **MAY** support additional session methods, prompt content types, and MCP transports by specifying additional diff --git a/docs/protocol/v2/draft/session-delete.mdx b/docs/protocol/v2/draft/session-delete.mdx index 992249191..3b8fd9bd0 100644 --- a/docs/protocol/v2/draft/session-delete.mdx +++ b/docs/protocol/v2/draft/session-delete.mdx @@ -85,5 +85,5 @@ On success, the Agent returns an empty result: - Deleted sessions no longer appear in future `session/list` results. - Deleting an already-deleted session, or a session that never existed, **SHOULD** succeed silently. - Agents may implement soft delete or hard delete. ACP only specifies the user-facing session-list behavior. -- Behavior for `session/load` on a deleted session is implementation-defined. +- Behavior for `session/resume` on a deleted session is implementation-defined. - Behavior for deleting an active session is implementation-defined. diff --git a/docs/protocol/v2/draft/session-list.mdx b/docs/protocol/v2/draft/session-list.mdx index 68f01a678..e5b23c45e 100644 --- a/docs/protocol/v2/draft/session-list.mdx +++ b/docs/protocol/v2/draft/session-list.mdx @@ -22,9 +22,9 @@ sequenceDiagram Agent-->>Client: session/list response (sessions) alt User selects a session - Client->>Agent: session/load (sessionId) + Client->>Agent: session/resume (sessionId, replayFrom) Note over Agent,Client: Replay conversation history... - Agent-->>Client: session/load response + Agent-->>Client: session/resume response end Note over Client,Agent: Ready for prompts @@ -202,4 +202,4 @@ after the first meaningful exchange to auto-generate a title. 1. Client calls `session/list` to discover available sessions 2. User selects a session from the list -3. Client calls [`session/load`](/protocol/v2/draft/session-setup#loading-sessions) with the chosen `sessionId` to resume the conversation +3. Client calls [`session/resume`](/protocol/v2/draft/session-setup#resuming-sessions) with the chosen `sessionId` to resume the conversation, optionally setting `replayFrom` to request history replay diff --git a/docs/protocol/v2/draft/session-setup.mdx b/docs/protocol/v2/draft/session-setup.mdx index af4f65704..9a6fcf536 100644 --- a/docs/protocol/v2/draft/session-setup.mdx +++ b/docs/protocol/v2/draft/session-setup.mdx @@ -1,6 +1,6 @@ --- title: "Session Setup" -description: "Creating and loading sessions" +description: "Creating and resuming sessions" --- Sessions represent a specific conversation or thread between the [Client](/protocol/v2/draft/overview#client) and [Agent](/protocol/v2/draft/overview#agent). Each session maintains its own context, conversation history, and state, allowing multiple independent interactions with the same Agent. @@ -23,19 +23,16 @@ sequenceDiagram Note over Agent: Create session context Note over Agent: Connect to MCP servers Agent-->>Client: session/new response (sessionId) - else - Client->>Agent: session/load (sessionId) - Note over Agent: Restore session context - Note over Agent: Connect to MCP servers - Note over Agent,Client: Replay conversation history... - Agent->>Client: session/update - Agent->>Client: session/update - Note over Agent,Client: All content reported - Agent-->>Client: session/load response else Client->>Agent: session/resume (sessionId) Note over Agent: Restore session context Note over Agent: Connect to MCP servers + opt replayFrom: start + Note over Agent,Client: Replay conversation history... + Agent->>Client: session/update + Agent->>Client: session/update + Note over Agent,Client: All content reported + end Agent-->>Client: session/resume response end @@ -83,24 +80,27 @@ The Agent **MUST** respond with a unique [Session ID](#session-id) that identifi } ``` -## Loading Sessions +## Resuming Sessions Agents that support the `session` method surface **MUST** support -`session/load`, allowing Clients to resume previous conversations with history -replay. This feature enables persistence across restarts and sharing sessions -between different Client instances. +`session/resume`, allowing Clients to reconnect to existing sessions. By +default, resume restores the session context without replaying prior +conversation history. Clients that need history replay can request it with +`replayFrom`. -To load an existing session, Clients **MUST** call the `session/load` method with: +To resume an existing session, Clients **MUST** call the `session/resume` method +with: - The [Session ID](#session-id) to resume - [MCP servers](#mcp-servers) to connect to - The [working directory](#working-directory) +- Optionally, a `replayFrom` cursor ```json { "jsonrpc": "2.0", - "id": 1, - "method": "session/load", + "id": 2, + "method": "session/resume", "params": { "sessionId": "sess_789xyz", "cwd": "/home/user/project", @@ -117,7 +117,36 @@ To load an existing session, Clients **MUST** call the `session/load` method wit } ``` -The Agent **MUST** replay the entire conversation to the Client in the form of `session/update` notifications (like `session/prompt`). User, agent, and thought messages can each be replayed as message updates with full `content` arrays or as chunks. +When `replayFrom` is omitted or `null`, the Agent **MUST NOT** replay the +conversation history via `session/update` notifications before responding. It +restores the session context, reconnects to the requested MCP servers, and +returns once the session is ready to continue. + +To request full history replay, Clients set `replayFrom` to +`{ "type": "start" }`: + +Replay cursors are inclusive: replay includes the position identified by the +cursor. For `{ "type": "start" }`, that means replaying the whole conversation. + +```json +{ + "jsonrpc": "2.0", + "id": 3, + "method": "session/resume", + "params": { + "sessionId": "sess_789xyz", + "cwd": "/home/user/project", + "replayFrom": { + "type": "start" + } + } +} +``` + +When `replayFrom.type` is `"start"`, the Agent **MUST** replay the entire +conversation to the Client in the form of `session/update` notifications before +responding. User, agent, and thought messages can each be replayed as message +updates with full `content` arrays or as chunks. For example, a user message from the conversation history: @@ -163,70 +192,25 @@ 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. +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 other optional 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. +When all requested replay entries have been reported to the Client, the Agent +**MUST** respond to the original `session/resume` request. ```json { "jsonrpc": "2.0", - "id": 1, - "result": {} -} -``` - -The response **MAY** also include initial session configuration state when that -feature is supported by the Agent. - -The Client can then continue sending prompts as if the session was never -interrupted. - -## Resuming Sessions - -Agents that support the `session` method surface **MUST** support -`session/resume`, allowing Clients to reconnect to an existing session without -replaying the conversation history. - -To resume an existing session without replaying prior messages, Clients -**MUST** call the `session/resume` method with: - -- The [Session ID](#session-id) to resume -- [MCP servers](#mcp-servers) to connect to -- The [working directory](#working-directory) - -```json -{ - "jsonrpc": "2.0", - "id": 2, - "method": "session/resume", - "params": { - "sessionId": "sess_789xyz", - "cwd": "/home/user/project", - "mcpServers": [ - { - "type": "stdio", - "name": "workspace-tools", - "command": "/path/to/mcp-server", - "args": ["--mode", "workspace"], - "env": [] - } - ] - } -} -``` - -Unlike `session/load`, the Agent **MUST NOT** replay the conversation history -via `session/update` notifications before responding. Instead, it restores the -session context, reconnects to the requested MCP servers, and returns once the -session is ready to continue. - -```json -{ - "jsonrpc": "2.0", - "id": 2, + "id": 3, "result": {} } ``` @@ -280,13 +264,13 @@ active. Agents that advertise `session.additionalDirectories` allow Clients to include `additionalDirectories` on supported session lifecycle requests to expand the session's effective workspace root set. Supported stable lifecycle -requests include `session/new`, `session/load`, and `session/resume`. +requests include `session/new` and `session/resume`. ```json { "jsonrpc": "2.0", "id": 2, - "method": "session/load", + "method": "session/resume", "params": { "sessionId": "sess_789xyz", "cwd": "/home/user/project", @@ -303,7 +287,7 @@ When present, `additionalDirectories` has the following behavior: - `cwd` remains the primary working directory and the base for relative paths - each `additionalDirectories` entry **MUST** be an absolute path - omitting the field or providing an empty array activates no additional roots for the resulting session -- on `session/load` and `session/resume`, Clients must send the full intended additional-root list again; that list may differ from any previous or reported list as long as the request `cwd` matches the session's `cwd`, and omitting the field or providing an empty array does not restore stored roots implicitly +- on `session/resume`, Clients must send the full intended additional-root list again; that list may differ from any previous or reported list as long as the request `cwd` matches the session's `cwd`, and omitting the field or providing an empty array does not restore stored roots implicitly Clients **MUST** only send `additionalDirectories` when the Agent advertises `session.additionalDirectories`. @@ -315,7 +299,6 @@ Clients use this ID to: - Send prompt requests via `session/prompt` - Cancel ongoing operations via `session/cancel` -- Load previous sessions via `session/load` - Resume previous sessions via `session/resume` - Close active sessions via `session/close` diff --git a/docs/protocol/v2/draft/transports.mdx b/docs/protocol/v2/draft/transports.mdx index 20a9174f6..0224c8708 100644 --- a/docs/protocol/v2/draft/transports.mdx +++ b/docs/protocol/v2/draft/transports.mdx @@ -75,7 +75,7 @@ When receiving a batch call: fail. Clients and agents **SHOULD NOT** batch lifecycle-sensitive messages such as -`initialize`, `auth/login`, `session/new`, `session/load`, and +`initialize`, `auth/login`, `session/new`, `session/resume`, and `session/prompt`. These messages are easier to reason about as individual transport messages and can change which later messages are valid. diff --git a/docs/protocol/v2/initialization.mdx b/docs/protocol/v2/initialization.mdx index 563d9aedd..7d7bf8d3b 100644 --- a/docs/protocol/v2/initialization.mdx +++ b/docs/protocol/v2/initialization.mdx @@ -119,9 +119,9 @@ The Agent **SHOULD** specify whether it supports the following capabilities: The Agent supports the `session/*` method surface. Omitted or `null` means the Agent does not support session methods. Supplying `{}` means the Agent - supports the baseline session methods: `session/new`, `session/load`, - `session/list`, `session/resume`, `session/close`, `session/prompt`, - `session/cancel`, and `session/update`. + supports the baseline session methods: `session/new`, `session/list`, + `session/resume`, `session/close`, `session/prompt`, `session/cancel`, and + `session/update`. @@ -131,8 +131,8 @@ The Agent **SHOULD** specify whether it supports the following capabilities: #### Session Capabilities Supplying `session: {}` means the Agent supports `session/new`, -`session/load`, `session/list`, `session/resume`, `session/close`, -`session/prompt`, `session/cancel`, and `session/update`. +`session/list`, `session/resume`, `session/close`, `session/prompt`, +`session/cancel`, and `session/update`. Optionally, Agents **MAY** support prompt extensions, MCP server transports, and additional session methods such as `session/delete` by specifying nested diff --git a/docs/protocol/v2/overview.mdx b/docs/protocol/v2/overview.mdx index e7f632f93..aa93a1b44 100644 --- a/docs/protocol/v2/overview.mdx +++ b/docs/protocol/v2/overview.mdx @@ -31,7 +31,7 @@ A typical flow follows this pattern: - Client → Agent: `session/new` to create a new session -- Client → Agent: `session/load` or `session/resume` to resume an existing session +- Client → Agent: `session/resume` to resume an existing session @@ -89,13 +89,6 @@ Agents are programs that use generative AI to autonomously modify code. They typ Agent. -Schema]} -> - [Load an existing session](/protocol/v2/session-setup#loading-sessions). - - Schema]} @@ -107,7 +100,7 @@ Agents are programs that use generative AI to autonomously modify code. They typ name="session/resume" post={[Schema]} > - [Resume an existing session without replaying + [Resume an existing session, optionally replaying history](/protocol/v2/session-setup#resuming-sessions). diff --git a/docs/protocol/v2/schema.mdx b/docs/protocol/v2/schema.mdx index ed546af5b..94e87708d 100644 --- a/docs/protocol/v2/schema.mdx +++ b/docs/protocol/v2/schema.mdx @@ -389,76 +389,6 @@ to fetch the next page. If absent, there are no more results. Array of session information objects. - -### session/load - -Loads an existing session to resume a previous conversation. - -The agent should: - -- Restore the session context and conversation history -- Connect to the specified MCP servers -- Stream the entire conversation history back to the client via notifications - -See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/v2/session-setup#loading-sessions) - -#### LoadSessionRequest - -Request parameters for loading an existing session. - -See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/v2/session-setup#loading-sessions) - -**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/v2/extensibility) - - -"string"[]} > - Additional workspace roots to activate for this session. Each path must be absolute. - -When omitted or empty, no additional roots are activated. When non-empty, -this is the complete resulting additional-root list for the loaded -session. It may differ from any previously used or reported list as long as -the request `cwd` matches the session's `cwd`. - - - - The working directory for this session. Must be an absolute path. - -McpServer[]} > - List of MCP servers to connect to for this session. - -SessionId} required> - The ID of the session to load. - - -#### LoadSessionResponse - -Response from loading an existing session. - -**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/v2/extensibility) - - -SessionConfigOption[]} > - Initial session configuration options. - - ### session/new @@ -618,18 +548,18 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e ### session/resume -Resumes an existing session without returning previous messages. +Resumes an existing session. -The agent should resume the session context, allowing the conversation to continue -without replaying the message history (unlike `session/load`). +The agent should resume the session context, allowing the conversation +to continue. If `replayFrom` is set, the agent should replay +conversation history before responding. #### ResumeSessionRequest Request parameters for resuming an existing session. -Resumes an existing session without returning previous messages (unlike `session/load`). -This is useful when a Client already has the session history and only needs -to reconnect to the Agent's session state. +Resumes an existing session and optionally replays prior conversation +history according to `replayFrom`. **Type:** Object @@ -657,6 +587,16 @@ the request `cwd` matches the session's `cwd`. McpServer[]} > List of MCP servers to connect to for this session. + +ReplayFrom | null} > + Inclusive cursor describing where conversation replay should begin. + +Optional. Omitted or `null` both mean the Agent should resume without +replaying previous conversation history. Replay cursors are inclusive: +replay includes the position identified by the cursor. Supplying +`\{ "type": "start" \}` means the Agent should replay the whole +conversation before responding. + SessionId} required> The ID of the session to resume. @@ -2710,6 +2650,84 @@ Non-breaking changes should be introduced via capabilities. | Minimum | `0` | | Maximum | `65535` | +## ReplayFrom + +Inclusive cursor describing where replayed session history should begin. + +Replay includes the position identified by the cursor. + +**Type:** Union + + +Replay the whole conversation from its first replayable entry. + + + + + 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/v2/extensibility) + + + + The discriminator value. Must be `"start"`. + + + + + + +Custom or future replay cursor. + +Values beginning with `_` are reserved for implementation-specific +extensions. Unknown values that do not begin with `_` are reserved for +future ACP variants. + +Receivers that do not understand this cursor should preserve the raw +payload when storing, replaying, proxying, or forwarding requests, and +otherwise reject the request rather than guessing where to replay from. + + + + + 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/v2/extensibility) + + + + Custom or future replay cursor type. + +Values beginning with `_` are reserved for implementation-specific +extensions. Unknown values that do not begin with `_` are reserved for +future ACP variants. + + + + + + +## ReplayFromStart + +Inclusive replay cursor requesting replay from the start of the conversation. + +**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/v2/extensibility) + + + ## RequestId JSON RPC Request Id @@ -3005,9 +3023,8 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e Session capabilities supported by the agent. Supplying `\{\}` means the agent supports the baseline session methods: -`session/new`, `session/load`, `session/list`, `session/resume`, -`session/close`, `session/prompt`, `session/cancel`, and -`session/update`. +`session/new`, `session/list`, `session/resume`, `session/close`, +`session/prompt`, `session/cancel`, and `session/update`. Agents that support sessions **MAY** support additional session methods, prompt content types, and MCP transports by specifying additional diff --git a/docs/protocol/v2/session-delete.mdx b/docs/protocol/v2/session-delete.mdx index f1396e7cd..36db4b447 100644 --- a/docs/protocol/v2/session-delete.mdx +++ b/docs/protocol/v2/session-delete.mdx @@ -85,5 +85,5 @@ On success, the Agent returns an empty result: - Deleted sessions no longer appear in future `session/list` results. - Deleting an already-deleted session, or a session that never existed, **SHOULD** succeed silently. - Agents may implement soft delete or hard delete. ACP only specifies the user-facing session-list behavior. -- Behavior for `session/load` on a deleted session is implementation-defined. +- Behavior for `session/resume` on a deleted session is implementation-defined. - Behavior for deleting an active session is implementation-defined. diff --git a/docs/protocol/v2/session-list.mdx b/docs/protocol/v2/session-list.mdx index dc2aaefbf..38310bcf1 100644 --- a/docs/protocol/v2/session-list.mdx +++ b/docs/protocol/v2/session-list.mdx @@ -22,9 +22,9 @@ sequenceDiagram Agent-->>Client: session/list response (sessions) alt User selects a session - Client->>Agent: session/load (sessionId) + Client->>Agent: session/resume (sessionId, replayFrom) Note over Agent,Client: Replay conversation history... - Agent-->>Client: session/load response + Agent-->>Client: session/resume response end Note over Client,Agent: Ready for prompts @@ -202,4 +202,4 @@ after the first meaningful exchange to auto-generate a title. 1. Client calls `session/list` to discover available sessions 2. User selects a session from the list -3. Client calls [`session/load`](/protocol/v2/session-setup#loading-sessions) with the chosen `sessionId` to resume the conversation +3. Client calls [`session/resume`](/protocol/v2/session-setup#resuming-sessions) with the chosen `sessionId` to resume the conversation, optionally setting `replayFrom` to request history replay diff --git a/docs/protocol/v2/session-setup.mdx b/docs/protocol/v2/session-setup.mdx index 98ab83327..bc971c6ce 100644 --- a/docs/protocol/v2/session-setup.mdx +++ b/docs/protocol/v2/session-setup.mdx @@ -1,6 +1,6 @@ --- title: "Session Setup" -description: "Creating and loading sessions" +description: "Creating and resuming sessions" --- Sessions represent a specific conversation or thread between the [Client](/protocol/v2/overview#client) and [Agent](/protocol/v2/overview#agent). Each session maintains its own context, conversation history, and state, allowing multiple independent interactions with the same Agent. @@ -21,19 +21,16 @@ sequenceDiagram Note over Agent: Create session context Note over Agent: Connect to MCP servers Agent-->>Client: session/new response (sessionId) - else - Client->>Agent: session/load (sessionId) - Note over Agent: Restore session context - Note over Agent: Connect to MCP servers - Note over Agent,Client: Replay conversation history... - Agent->>Client: session/update - Agent->>Client: session/update - Note over Agent,Client: All content reported - Agent-->>Client: session/load response else Client->>Agent: session/resume (sessionId) Note over Agent: Restore session context Note over Agent: Connect to MCP servers + opt replayFrom: start + Note over Agent,Client: Replay conversation history... + Agent->>Client: session/update + Agent->>Client: session/update + Note over Agent,Client: All content reported + end Agent-->>Client: session/resume response end @@ -81,24 +78,27 @@ The Agent **MUST** respond with a unique [Session ID](#session-id) that identifi } ``` -## Loading Sessions +## Resuming Sessions Agents that support the `session` method surface **MUST** support -`session/load`, allowing Clients to resume previous conversations. This feature -enables persistence across restarts and sharing sessions between different -Client instances. +`session/resume`, allowing Clients to reconnect to existing sessions. By +default, resume restores the session context without replaying prior +conversation history. Clients that need history replay can request it with +`replayFrom`. -To load an existing session, Clients **MUST** call the `session/load` method with: +To resume an existing session, Clients **MUST** call the `session/resume` method +with: - The [Session ID](#session-id) to resume - [MCP servers](#mcp-servers) to connect to - The [working directory](#working-directory) +- Optionally, a `replayFrom` cursor ```json { "jsonrpc": "2.0", - "id": 1, - "method": "session/load", + "id": 2, + "method": "session/resume", "params": { "sessionId": "sess_789xyz", "cwd": "/home/user/project", @@ -115,7 +115,36 @@ To load an existing session, Clients **MUST** call the `session/load` method wit } ``` -The Agent **MUST** replay the entire conversation to the Client in the form of `session/update` notifications (like `session/prompt`). User, agent, and thought messages can each be replayed as message updates with full `content` arrays or as chunks. +When `replayFrom` is omitted or `null`, the Agent **MUST NOT** replay the +conversation history via `session/update` notifications before responding. It +restores the session context, reconnects to the requested MCP servers, and +returns once the session is ready to continue. + +To request full history replay, Clients set `replayFrom` to +`{ "type": "start" }`: + +Replay cursors are inclusive: replay includes the position identified by the +cursor. For `{ "type": "start" }`, that means replaying the whole conversation. + +```json +{ + "jsonrpc": "2.0", + "id": 3, + "method": "session/resume", + "params": { + "sessionId": "sess_789xyz", + "cwd": "/home/user/project", + "replayFrom": { + "type": "start" + } + } +} +``` + +When `replayFrom.type` is `"start"`, the Agent **MUST** replay the entire +conversation to the Client in the form of `session/update` notifications before +responding. User, agent, and thought messages can each be replayed as message +updates with full `content` arrays or as chunks. For example, a user message from the conversation history: @@ -161,64 +190,25 @@ 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. +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 other optional 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. +When all requested replay entries have been reported to the Client, the Agent +**MUST** respond to the original `session/resume` request. ```json { "jsonrpc": "2.0", - "id": 1, - "result": {} -} -``` - -The response **MAY** also include initial session configuration state when that -feature is supported by the Agent. - -The Client can then continue sending prompts as if the session was never interrupted. - -## Resuming Sessions - -Agents that support the `session` method surface **MUST** support -`session/resume`, allowing Clients to reconnect to an existing session without -replaying the conversation history. - -To resume an existing session without replaying prior messages, Clients **MUST** call the `session/resume` method with: - -- The [Session ID](#session-id) to resume -- [MCP servers](#mcp-servers) to connect to -- The [working directory](#working-directory) - -```json -{ - "jsonrpc": "2.0", - "id": 2, - "method": "session/resume", - "params": { - "sessionId": "sess_789xyz", - "cwd": "/home/user/project", - "mcpServers": [ - { - "type": "stdio", - "name": "workspace-tools", - "command": "/path/to/mcp-server", - "args": ["--mode", "workspace"], - "env": [] - } - ] - } -} -``` - -Unlike `session/load`, the Agent **MUST NOT** replay the conversation history via `session/update` notifications before responding. Instead, it restores the session context, reconnects to the requested MCP servers, and returns once the session is ready to continue. - -```json -{ - "jsonrpc": "2.0", - "id": 2, + "id": 3, "result": {} } ``` @@ -268,13 +258,13 @@ Agents MAY return an error if the session does not exist or is not currently act Agents that advertise `session.additionalDirectories` allow Clients to include `additionalDirectories` on supported session lifecycle requests to expand the session's effective workspace root set. Supported stable lifecycle -requests include `session/new`, `session/load`, and `session/resume`. +requests include `session/new` and `session/resume`. ```json { "jsonrpc": "2.0", "id": 2, - "method": "session/load", + "method": "session/resume", "params": { "sessionId": "sess_789xyz", "cwd": "/home/user/project", @@ -291,7 +281,7 @@ When present, `additionalDirectories` has the following behavior: - `cwd` remains the primary working directory and the base for relative paths - each `additionalDirectories` entry **MUST** be an absolute path - omitting the field or providing an empty array activates no additional roots for the resulting session -- on `session/load` and `session/resume`, Clients must send the full intended additional-root list again; that list may differ from any previous or reported list as long as the request `cwd` matches the session's `cwd`, and omitting the field or providing an empty array does not restore stored roots implicitly +- on `session/resume`, Clients must send the full intended additional-root list again; that list may differ from any previous or reported list as long as the request `cwd` matches the session's `cwd`, and omitting the field or providing an empty array does not restore stored roots implicitly Clients **MUST** only send `additionalDirectories` when the Agent advertises `session.additionalDirectories`. @@ -303,7 +293,6 @@ Clients use this ID to: - Send prompt requests via `session/prompt` - Cancel ongoing operations via `session/cancel` -- Load previous sessions via `session/load` - Resume previous sessions via `session/resume` - Close active sessions via `session/close` diff --git a/docs/protocol/v2/transports.mdx b/docs/protocol/v2/transports.mdx index 20a9174f6..0224c8708 100644 --- a/docs/protocol/v2/transports.mdx +++ b/docs/protocol/v2/transports.mdx @@ -75,7 +75,7 @@ When receiving a batch call: fail. Clients and agents **SHOULD NOT** batch lifecycle-sensitive messages such as -`initialize`, `auth/login`, `session/new`, `session/load`, and +`initialize`, `auth/login`, `session/new`, `session/resume`, and `session/prompt`. These messages are easier to reason about as individual transport messages and can change which later messages are valid. diff --git a/docs/rfds/v2/overview.mdx b/docs/rfds/v2/overview.mdx index 116766096..4730f69d0 100644 --- a/docs/rfds/v2/overview.mdx +++ b/docs/rfds/v2/overview.mdx @@ -31,6 +31,7 @@ Current RFDs with active maintainer focus that are targeting the v2 release - [New Prompt Lifecycle](./prompt.mdx) - [Enum Variant Extension](./enum-variant-extension.mdx) - [Required Session Methods](./required-session-methods.mdx) +- [Session Resume Replay](./session-resume-replay.mdx) - [Client Filesystem and Terminal Surface](./client-filesystem-terminal-capabilities.mdx) - [Plan Variants](./plan-variants.mdx) - [Tool Call Updates](./tool-call-updates.mdx) @@ -58,7 +59,8 @@ Other RFDs will progress separately and are not dependent on breaking changes (s - Group authentication methods under `auth/*`: v2 uses `auth/login` and `auth/logout` instead of v1's top-level `authenticate` and `logout` method names. `auth/logout` is required for v2 Agents and no longer uses a `capabilities.auth.logout` support marker. The generated request and response type names follow the grouped method naming as `LoginAuthRequest` / `LoginAuthResponse` and `LogoutAuthRequest` / `LogoutAuthResponse`. - Use concise capability group names such as `session` and `auth`, replacing names like `sessionCapabilities`. - Make `session` optional so non-session agents, such as NES-only agents, can omit it. - - Require the baseline session lifecycle methods when `session` is present: `session/new`, `session/load`, `session/list`, `session/resume`, `session/close`, `session/prompt`, `session/cancel`, and `session/update`. + - Require the baseline session lifecycle methods when `session` is present: `session/new`, `session/list`, `session/resume`, `session/close`, `session/prompt`, `session/cancel`, and `session/update`. + - Remove `session/load` from v2. `session/resume` handles both no-replay resume and full replay by using the optional `replayFrom` cursor from the [Session Resume Replay](./session-resume-replay.mdx) RFD. - Move optional session-scoped capability groups under `session`, including `prompt` and `mcp` as `session.prompt` and `session.mcp`. - Represent support markers as capability objects instead of booleans. Supplying `{}` means supported, while omission or `null` means unsupported. Booleans remain appropriate for actual data or configuration inside an already-advertised capability. - Align MCP server transports with the current MCP transport model: @@ -109,14 +111,15 @@ With the needed breaking changes, as much as possible I am targeting having a co - 2026-07-02: Moved the v2 RFD collection to Active. - 2026-07-02: Added the v2 Permission Requests RFD for required permission titles, optional structured subjects, and tool-call permission subjects. -- 2026-07-02: Added the v2 Required Session Methods RFD and recorded that `session/load`, `session/list`, `session/resume`, and `session/close` are baseline when `session` is present. +- 2026-07-02: Added the v2 Session Resume Replay RFD and recorded that `session/resume` replaces `session/load` in v2 by using optional `replayFrom` cursors. +- 2026-07-02: Added the v2 Required Session Methods RFD and recorded that `session/list`, `session/resume`, and `session/close` are baseline when `session` is present. - 2026-06-30: Recorded the v2 ID unification principle: prefer domain-specific ID field names. - 2026-06-25: Recorded the v2 initialize information cleanup: required role-agnostic `info` in both params and results, replacing `clientInfo` and `agentInfo`. - 2026-06-25: Recorded the v2 authentication method cleanup: grouped `auth/login` and required `auth/logout` method names replace v1's top-level `authenticate` and capability-gated `logout`, with matching generated type names for login and logout auth requests and responses. - 2026-06-09: Added tool-call content chunks for streaming individual `ToolCallContent` items. - 2026-06-09: Added the v2 Message Updates and Chunks RFD to define whole-message upserts alongside streamed message chunks. - 2026-06-08: Added the v2 Tool Call Updates RFD to make `tool_call_update` the single upsert-style tool-call session update. -- 2026-06-05: Recorded the v2 capability cleanup: unified initialize capability fields, optional `session` support for NES-only agents, session-scoped `prompt`, `mcp`, and `loadSession` capabilities, object-shaped support markers, explicit `session.mcp.stdio`, removal of deprecated MCP SSE transport, and tagged MCP server transport configs. +- 2026-06-05: Recorded the v2 capability cleanup: unified initialize capability fields, optional `session` support for NES-only agents, session-scoped `prompt` and `mcp` capabilities, object-shaped support markers, explicit `session.mcp.stdio`, removal of deprecated MCP SSE transport, and tagged MCP server transport configs. - 2026-06-02: Recorded the v2 decision to follow JSON-RPC 2.0 batch request and notification behavior. - 2026-06-02: Recorded the v2 decision to remove Client filesystem and terminal execution capabilities, methods, and terminal tool-call content. - 2026-06-02: Added the v2 Plan Variants RFD to make item-based `plan_update` the default v2 plan shape. diff --git a/docs/rfds/v2/prompt.mdx b/docs/rfds/v2/prompt.mdx index 8f4a53bf9..ac1e3c60d 100644 --- a/docs/rfds/v2/prompt.mdx +++ b/docs/rfds/v2/prompt.mdx @@ -185,7 +185,7 @@ We could explore adding which permission or elicitation it is waiting on if we w This isn't a huge schema change, but it is a fundamental behavior change in the protocol that I believe: - Provides agents with much more flexibility in how they want to update a client about a given session -- Solves some concrete pain points in the current model (i.e. how to integrate prompts into session/load and multi-client replays, message IDs, etc) +- Solves some concrete pain points in the current model (i.e. how to integrate prompts into session replay and multi-client replays, message IDs, etc) Ultimately, after months of using the current model, I am quite excited for what these small tweaks can unlock, and also how we can build on top of this in the future! diff --git a/docs/rfds/v2/required-session-methods.mdx b/docs/rfds/v2/required-session-methods.mdx index 118ba4fdf..d24452667 100644 --- a/docs/rfds/v2/required-session-methods.mdx +++ b/docs/rfds/v2/required-session-methods.mdx @@ -9,7 +9,7 @@ Author(s): [@benbrandt](https://github.com/benbrandt) > What are you proposing to change? For ACP v2, Agents that advertise the `session` capability should be required -to support the core session lifecycle methods: `session/load`, `session/list`, +to support the core session lifecycle methods: `session/list`, `session/resume`, and `session/close`. These methods should no longer have individual v2 capability markers. The @@ -25,7 +25,7 @@ In v1, several session lifecycle methods were added as optional capabilities: `sessionCapabilities.close`. For v2, we can set a cleaner baseline. If an Agent supports sessions, it should -support the lifecycle operations needed to create, discover, reconnect, load, +support the lifecycle operations needed to create, discover, reconnect, replay, prompt, cancel, update, and close them. In practice, almost all agents, especially the most popular ones, already support these, @@ -39,7 +39,6 @@ When an Agent includes `capabilities.session` in its v2 `initialize` response, the Agent **MUST** support these baseline session methods: - `session/new` -- `session/load` - `session/list` - `session/resume` - `session/close` @@ -47,8 +46,10 @@ the Agent **MUST** support these baseline session methods: - `session/cancel` - `session/update` -The v2 schema should remove the optional `session.load`, `session.list`, -`session.resume`, and `session.close` capability fields. +The v2 schema should remove the optional `session.list`, `session.resume`, and +`session.close` capability fields. The [Session Resume Replay](./session-resume-replay.mdx) +RFD removes `session/load` from v2 and folds history replay into +`session/resume`. The remaining optional session capabilities continue to represent optional features: diff --git a/docs/rfds/v2/session-resume-replay.mdx b/docs/rfds/v2/session-resume-replay.mdx new file mode 100644 index 000000000..4355fc554 --- /dev/null +++ b/docs/rfds/v2/session-resume-replay.mdx @@ -0,0 +1,93 @@ +--- +title: "v2 Session Resume Replay" +--- + +Author(s): [@benbrandt](https://github.com/benbrandt) + +## Elevator pitch + +> What are you proposing to change? + +ACP v2 should unify `session/load` and `session/resume` into a single +`session/resume` method. Clients that need replay can set a `replayFrom` cursor +on `session/resume`; clients that omit `replayFrom` get the current resume +behavior without history replay. + +## Status quo + +> How do things work today and what problems does this cause? Why would we change things? + +In v1, `session/load` and `session/resume` are separate methods. They restore +the same existing session context, reconnect MCP servers, and accept the same +workspace parameters. The main behavioral difference is replay: + +- `session/load` replays previous conversation history before responding. +- `session/resume` restores the session without replaying previous history. + +Keeping both methods in v2 preserves a distinction that is better represented as +an option on one restore operation. It also forces capability and transport docs +to explain two methods when the client is choosing between replay modes. + +## What we propose to do about it + +> What are you proposing to improve the situation? + +Remove `session/load` from the v2 method surface and keep `session/resume`. +`ResumeSessionRequest` gains an optional `replayFrom` field. + +Omitted `replayFrom` and `replayFrom: null` are equivalent: the Agent resumes +without replaying previous conversation history. + +The initial replay cursor is: + +```json +{ + "type": "start" +} +``` + +`replayFrom: { "type": "start" }` means the Agent replays the whole +conversation through `session/update` notifications before responding to the +`session/resume` request. + +Replay cursors are inclusive: replay includes the position identified by the +cursor. Future cursors that identify a message would replay that message before +continuing with later history. + +## Shiny future + +> How will things will play out once this feature exists? + +Clients always reconnect to existing sessions through `session/resume`. They +choose replay behavior by setting or omitting `replayFrom`, rather than choosing +between two different restore methods. + +This leaves room for future replay cursors, such as replaying from a specific +message, checkpoint, or server-provided cursor, without adding more session +restore methods. + +## Implementation details and plan + +> Tell me more about your implementation. What is your detailed implementation plan? + +1. Remove `session/load`, `LoadSessionRequest`, and `LoadSessionResponse` from + the v2 schema. +2. Add `replayFrom` to `ResumeSessionRequest` as an optional tagged union. +3. Add the initial `replayFrom` variant `{ "type": "start" }`. + +## Frequently asked questions + +> What questions have arisen over the course of authoring this document or during subsequent discussions? + +### Is `replayFrom` required? + +No. The field is optional. Omitted and `null` both mean no history replay. + +### Why use an object instead of a string? + +An object leaves room for future cursor forms that need fields. The initial +`start` form has no fields beyond `type`. + +## Revision history + +- 2026-07-02: Initial draft. diff --git a/schema/v2/meta.json b/schema/v2/meta.json index 1b42e980e..647276751 100644 --- a/schema/v2/meta.json +++ b/schema/v2/meta.json @@ -4,7 +4,6 @@ "initialize": "initialize", "auth_login": "auth/login", "session_new": "session/new", - "session_load": "session/load", "session_set_config_option": "session/set_config_option", "session_prompt": "session/prompt", "session_cancel": "session/cancel", diff --git a/schema/v2/meta.unstable.json b/schema/v2/meta.unstable.json index 211abc081..47d7973cb 100644 --- a/schema/v2/meta.unstable.json +++ b/schema/v2/meta.unstable.json @@ -7,7 +7,6 @@ "providers_set": "providers/set", "providers_disable": "providers/disable", "session_new": "session/new", - "session_load": "session/load", "session_set_config_option": "session/set_config_option", "session_prompt": "session/prompt", "session_cancel": "session/cancel", diff --git a/schema/v2/schema.json b/schema/v2/schema.json index 74a61c8c3..f9586cc27 100644 --- a/schema/v2/schema.json +++ b/schema/v2/schema.json @@ -188,15 +188,6 @@ } ] }, - { - "title": "LoadSessionResponse", - "description": "Successful result returned for a `session/load` request.", - "allOf": [ - { - "$ref": "#/$defs/LoadSessionResponse" - } - ] - }, { "title": "ListSessionsResponse", "description": "Successful result returned for a `session/list` request.", @@ -1644,15 +1635,6 @@ } ] }, - { - "title": "LoadSessionResponse", - "description": "Successful result returned for a `session/load` request.", - "allOf": [ - { - "$ref": "#/$defs/LoadSessionResponse" - } - ] - }, { "title": "ListSessionsResponse", "description": "Successful result returned for a `session/list` request.", @@ -1868,7 +1850,7 @@ } }, "SessionCapabilities": { - "description": "Session capabilities supported by the agent.\n\nSupplying `{}` means the agent supports the baseline session methods:\n`session/new`, `session/load`, `session/list`, `session/resume`,\n`session/close`, `session/prompt`, `session/cancel`, and\n`session/update`.\n\nAgents that support sessions **MAY** support additional session methods,\nprompt content types, and MCP transports by specifying additional\ncapabilities.\n\nSee protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#session-capabilities)", + "description": "Session capabilities supported by the agent.\n\nSupplying `{}` means the agent supports the baseline session methods:\n`session/new`, `session/list`, `session/resume`, `session/close`,\n`session/prompt`, `session/cancel`, and `session/update`.\n\nAgents that support sessions **MAY** support additional session methods,\nprompt content types, and MCP transports by specifying additional\ncapabilities.\n\nSee protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#session-capabilities)", "type": "object", "properties": { "prompt": { @@ -2512,29 +2494,6 @@ }, "required": ["currentValue", "options"] }, - "LoadSessionResponse": { - "description": "Response from loading an existing session.", - "type": "object", - "properties": { - "configOptions": { - "description": "Initial session configuration options.", - "type": "array", - "items": { - "$ref": "#/$defs/SessionConfigOption" - }, - "x-deserialize-default-on-error": true, - "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)", - "type": ["object", "null"], - "x-deserialize-default-on-error": true, - "additionalProperties": true - } - }, - "x-side": "agent", - "x-method": "session/load" - }, "ListSessionsResponse": { "description": "Response from listing sessions.", "type": "object", @@ -4095,15 +4054,6 @@ } ] }, - { - "title": "LoadSessionRequest", - "description": "Loads an existing session to resume a previous conversation.\n\nThe agent should:\n- Restore the session context and conversation history\n- Connect to the specified MCP servers\n- Stream the entire conversation history back to the client via notifications\n\nSee protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/v2/session-setup#loading-sessions)", - "allOf": [ - { - "$ref": "#/$defs/LoadSessionRequest" - } - ] - }, { "title": "ListSessionsRequest", "description": "Lists existing sessions known to the agent.\n\nThe agent should return metadata about sessions with optional filtering and pagination support.", @@ -4124,7 +4074,7 @@ }, { "title": "ResumeSessionRequest", - "description": "Resumes an existing session without returning previous messages.\n\nThe agent should resume the session context, allowing the conversation to continue\nwithout replaying the message history (unlike `session/load`).", + "description": "Resumes an existing session.\n\nThe agent should resume the session context, allowing the conversation\nto continue. If `replayFrom` is set, the agent should replay\nconversation history before responding.", "allOf": [ { "$ref": "#/$defs/ResumeSessionRequest" @@ -4494,51 +4444,6 @@ }, "required": ["name", "command"] }, - "LoadSessionRequest": { - "description": "Request parameters for loading an existing session.\n\nSee protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/v2/session-setup#loading-sessions)", - "type": "object", - "properties": { - "sessionId": { - "description": "The ID of the session to load.", - "allOf": [ - { - "$ref": "#/$defs/SessionId" - } - ] - }, - "cwd": { - "description": "The working directory for this session. Must be an absolute path.", - "type": "string" - }, - "additionalDirectories": { - "description": "Additional workspace roots to activate for this session. Each path must be absolute.\n\nWhen omitted or empty, no additional roots are activated. When non-empty,\nthis is the complete resulting additional-root list for the loaded\nsession. It may differ from any previously used or reported list as long as\nthe request `cwd` matches the session's `cwd`.", - "type": "array", - "items": { - "type": "string" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true - }, - "mcpServers": { - "description": "List of MCP servers to connect to for this session.", - "type": "array", - "items": { - "$ref": "#/$defs/McpServer" - }, - "x-deserialize-default-on-error": true, - "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)", - "type": ["object", "null"], - "x-deserialize-default-on-error": true, - "additionalProperties": true - } - }, - "required": ["sessionId", "cwd"], - "x-side": "agent", - "x-method": "session/load" - }, "ListSessionsRequest": { "description": "Request parameters for listing existing sessions.", "type": "object", @@ -4587,7 +4492,7 @@ "x-method": "session/delete" }, "ResumeSessionRequest": { - "description": "Request parameters for resuming an existing session.\n\nResumes an existing session without returning previous messages (unlike `session/load`).\nThis is useful when a Client already has the session history and only needs\nto reconnect to the Agent's session state.", + "description": "Request parameters for resuming an existing session.\n\nResumes an existing session and optionally replays prior conversation\nhistory according to `replayFrom`.", "type": "object", "properties": { "sessionId": { @@ -4620,6 +4525,18 @@ "x-deserialize-default-on-error": true, "x-deserialize-skip-invalid-items": true }, + "replayFrom": { + "description": "Inclusive cursor describing where conversation replay should begin.\n\nOptional. Omitted or `null` both mean the Agent should resume without\nreplaying previous conversation history. Replay cursors are inclusive:\nreplay includes the position identified by the cursor. Supplying\n`{ \"type\": \"start\" }` means the Agent should replay the whole\nconversation before responding.", + "anyOf": [ + { + "$ref": "#/$defs/ReplayFrom" + }, + { + "type": "null" + } + ], + "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)", "type": ["object", "null"], @@ -4631,6 +4548,75 @@ "x-side": "agent", "x-method": "session/resume" }, + "ReplayFrom": { + "description": "Inclusive cursor describing where replayed session history should begin.\n\nReplay includes the position identified by the cursor.", + "anyOf": [ + { + "description": "Replay the whole conversation from its first replayable entry.", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "start" + } + }, + "required": ["type"], + "allOf": [ + { + "$ref": "#/$defs/ReplayFromStart" + } + ] + }, + { + "title": "other", + "description": "Custom or future replay cursor.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.\n\nReceivers that do not understand this cursor should preserve the raw\npayload when storing, replaying, proxying, or forwarding requests, and\notherwise reject the request rather than guessing where to replay from.", + "type": "object", + "properties": { + "type": { + "description": "Custom or future replay cursor type.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", + "type": "string" + }, + "_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)", + "type": ["object", "null"], + "x-deserialize-default-on-error": true, + "additionalProperties": true + } + }, + "required": ["type"], + "not": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "start" + } + }, + "required": ["type"] + } + ] + }, + "additionalProperties": true + } + ], + "discriminator": { + "propertyName": "type" + } + }, + "ReplayFromStart": { + "description": "Inclusive replay cursor requesting replay from the start of the conversation.", + "type": "object", + "properties": { + "_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)", + "type": ["object", "null"], + "x-deserialize-default-on-error": true, + "additionalProperties": true + } + } + }, "CloseSessionRequest": { "description": "Request parameters for closing an active session.\n\nThe agent **must** cancel any ongoing work related to the session (treat it\nas if `session/cancel` was called) and then free up any resources associated\nwith the session.", "type": "object", diff --git a/schema/v2/schema.unstable.json b/schema/v2/schema.unstable.json index 2ba4f2832..671c2c608 100644 --- a/schema/v2/schema.unstable.json +++ b/schema/v2/schema.unstable.json @@ -215,15 +215,6 @@ } ] }, - { - "title": "LoadSessionResponse", - "description": "Successful result returned for a `session/load` request.", - "allOf": [ - { - "$ref": "#/$defs/LoadSessionResponse" - } - ] - }, { "title": "ListSessionsResponse", "description": "Successful result returned for a `session/list` request.", @@ -2743,15 +2734,6 @@ } ] }, - { - "title": "LoadSessionResponse", - "description": "Successful result returned for a `session/load` request.", - "allOf": [ - { - "$ref": "#/$defs/LoadSessionResponse" - } - ] - }, { "title": "ListSessionsResponse", "description": "Successful result returned for a `session/list` request.", @@ -3048,7 +3030,7 @@ } }, "SessionCapabilities": { - "description": "Session capabilities supported by the agent.\n\nSupplying `{}` means the agent supports the baseline session methods:\n`session/new`, `session/load`, `session/list`, `session/resume`,\n`session/close`, `session/prompt`, `session/cancel`, and\n`session/update`.\n\nAgents that support sessions **MAY** support additional session methods,\nprompt content types, and MCP transports by specifying additional\ncapabilities.\n\nSee protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/v2/draft/initialization#session-capabilities)", + "description": "Session capabilities supported by the agent.\n\nSupplying `{}` means the agent supports the baseline session methods:\n`session/new`, `session/list`, `session/resume`, `session/close`,\n`session/prompt`, `session/cancel`, and `session/update`.\n\nAgents that support sessions **MAY** support additional session methods,\nprompt content types, and MCP transports by specifying additional\ncapabilities.\n\nSee protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/v2/draft/initialization#session-capabilities)", "type": "object", "properties": { "prompt": { @@ -4563,29 +4545,6 @@ }, "required": ["currentValue"] }, - "LoadSessionResponse": { - "description": "Response from loading an existing session.", - "type": "object", - "properties": { - "configOptions": { - "description": "Initial session configuration options.", - "type": "array", - "items": { - "$ref": "#/$defs/SessionConfigOption" - }, - "x-deserialize-default-on-error": true, - "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)", - "type": ["object", "null"], - "x-deserialize-default-on-error": true, - "additionalProperties": true - } - }, - "x-side": "agent", - "x-method": "session/load" - }, "ListSessionsResponse": { "description": "Response from listing sessions.", "type": "object", @@ -6912,15 +6871,6 @@ } ] }, - { - "title": "LoadSessionRequest", - "description": "Loads an existing session to resume a previous conversation.\n\nThe agent should:\n- Restore the session context and conversation history\n- Connect to the specified MCP servers\n- Stream the entire conversation history back to the client via notifications\n\nSee protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/v2/draft/session-setup#loading-sessions)", - "allOf": [ - { - "$ref": "#/$defs/LoadSessionRequest" - } - ] - }, { "title": "ListSessionsRequest", "description": "Lists existing sessions known to the agent.\n\nThe agent should return metadata about sessions with optional filtering and pagination support.", @@ -6950,7 +6900,7 @@ }, { "title": "ResumeSessionRequest", - "description": "Resumes an existing session without returning previous messages.\n\nThe agent should resume the session context, allowing the conversation to continue\nwithout replaying the message history (unlike `session/load`).", + "description": "Resumes an existing session.\n\nThe agent should resume the session context, allowing the conversation\nto continue. If `replayFrom` is set, the agent should replay\nconversation history before responding.", "allOf": [ { "$ref": "#/$defs/ResumeSessionRequest" @@ -7691,51 +7641,6 @@ }, "required": ["name", "command"] }, - "LoadSessionRequest": { - "description": "Request parameters for loading an existing session.\n\nSee protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/v2/draft/session-setup#loading-sessions)", - "type": "object", - "properties": { - "sessionId": { - "description": "The ID of the session to load.", - "allOf": [ - { - "$ref": "#/$defs/SessionId" - } - ] - }, - "cwd": { - "description": "The working directory for this session. Must be an absolute path.", - "type": "string" - }, - "additionalDirectories": { - "description": "Additional workspace roots to activate for this session. Each path must be absolute.\n\nWhen omitted or empty, no additional roots are activated. When non-empty,\nthis is the complete resulting additional-root list for the loaded\nsession. It may differ from any previously used or reported list as long as\nthe request `cwd` matches the session's `cwd`.", - "type": "array", - "items": { - "type": "string" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true - }, - "mcpServers": { - "description": "List of MCP servers to connect to for this session.", - "type": "array", - "items": { - "$ref": "#/$defs/McpServer" - }, - "x-deserialize-default-on-error": true, - "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)", - "type": ["object", "null"], - "x-deserialize-default-on-error": true, - "additionalProperties": true - } - }, - "required": ["sessionId", "cwd"], - "x-side": "agent", - "x-method": "session/load" - }, "ListSessionsRequest": { "description": "Request parameters for listing existing sessions.", "type": "object", @@ -7829,7 +7734,7 @@ "x-method": "session/fork" }, "ResumeSessionRequest": { - "description": "Request parameters for resuming an existing session.\n\nResumes an existing session without returning previous messages (unlike `session/load`).\nThis is useful when a Client already has the session history and only needs\nto reconnect to the Agent's session state.", + "description": "Request parameters for resuming an existing session.\n\nResumes an existing session and optionally replays prior conversation\nhistory according to `replayFrom`.", "type": "object", "properties": { "sessionId": { @@ -7862,6 +7767,18 @@ "x-deserialize-default-on-error": true, "x-deserialize-skip-invalid-items": true }, + "replayFrom": { + "description": "Inclusive cursor describing where conversation replay should begin.\n\nOptional. Omitted or `null` both mean the Agent should resume without\nreplaying previous conversation history. Replay cursors are inclusive:\nreplay includes the position identified by the cursor. Supplying\n`{ \"type\": \"start\" }` means the Agent should replay the whole\nconversation before responding.", + "anyOf": [ + { + "$ref": "#/$defs/ReplayFrom" + }, + { + "type": "null" + } + ], + "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)", "type": ["object", "null"], @@ -7873,6 +7790,75 @@ "x-side": "agent", "x-method": "session/resume" }, + "ReplayFrom": { + "description": "Inclusive cursor describing where replayed session history should begin.\n\nReplay includes the position identified by the cursor.", + "anyOf": [ + { + "description": "Replay the whole conversation from its first replayable entry.", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "start" + } + }, + "required": ["type"], + "allOf": [ + { + "$ref": "#/$defs/ReplayFromStart" + } + ] + }, + { + "title": "other", + "description": "Custom or future replay cursor.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.\n\nReceivers that do not understand this cursor should preserve the raw\npayload when storing, replaying, proxying, or forwarding requests, and\notherwise reject the request rather than guessing where to replay from.", + "type": "object", + "properties": { + "type": { + "description": "Custom or future replay cursor type.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", + "type": "string" + }, + "_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)", + "type": ["object", "null"], + "x-deserialize-default-on-error": true, + "additionalProperties": true + } + }, + "required": ["type"], + "not": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "start" + } + }, + "required": ["type"] + } + ] + }, + "additionalProperties": true + } + ], + "discriminator": { + "propertyName": "type" + } + }, + "ReplayFromStart": { + "description": "Inclusive replay cursor requesting replay from the start of the conversation.", + "type": "object", + "properties": { + "_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)", + "type": ["object", "null"], + "x-deserialize-default-on-error": true, + "additionalProperties": true + } + } + }, "CloseSessionRequest": { "description": "Request parameters for closing an active session.\n\nThe agent **must** cancel any ongoing work related to the session (treat it\nas if `session/cancel` was called) and then free up any resources associated\nwith the session.", "type": "object",