diff --git a/src/agent-client-protocol/src/agent.rs b/src/agent-client-protocol/src/agent.rs index eb40a93..3c1c423 100644 --- a/src/agent-client-protocol/src/agent.rs +++ b/src/agent-client-protocol/src/agent.rs @@ -10,6 +10,8 @@ use agent_client_protocol_schema::{ use agent_client_protocol_schema::{ForkSessionRequest, ForkSessionResponse}; #[cfg(feature = "unstable_session_list")] use agent_client_protocol_schema::{ListSessionsRequest, ListSessionsResponse}; +#[cfg(feature = "unstable_session_resume")] +use agent_client_protocol_schema::{ResumeSessionRequest, ResumeSessionResponse}; #[cfg(feature = "unstable_session_model")] use agent_client_protocol_schema::{SetSessionModelRequest, SetSessionModelResponse}; use serde_json::value::RawValue; @@ -154,6 +156,21 @@ pub trait Agent { Err(Error::method_not_found()) } + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Resumes an existing session without replaying message history. + /// + /// This is similar to `load_session`, except it does not return previous messages. + /// Useful for agents that support continuing conversations but don't store full history. + /// + /// Only available if the Agent supports the `sessionCapabilities.resume` capability. + #[cfg(feature = "unstable_session_resume")] + async fn resume_session(&self, _args: ResumeSessionRequest) -> Result { + Err(Error::method_not_found()) + } + /// Handles extension method requests from the client. /// /// Extension methods provide a way to add custom functionality while maintaining @@ -216,6 +233,10 @@ impl Agent for Rc { async fn fork_session(&self, args: ForkSessionRequest) -> Result { self.as_ref().fork_session(args).await } + #[cfg(feature = "unstable_session_resume")] + async fn resume_session(&self, args: ResumeSessionRequest) -> Result { + self.as_ref().resume_session(args).await + } async fn ext_method(&self, args: ExtRequest) -> Result { self.as_ref().ext_method(args).await } @@ -265,6 +286,10 @@ impl Agent for Arc { async fn fork_session(&self, args: ForkSessionRequest) -> Result { self.as_ref().fork_session(args).await } + #[cfg(feature = "unstable_session_resume")] + async fn resume_session(&self, args: ResumeSessionRequest) -> Result { + self.as_ref().resume_session(args).await + } async fn ext_method(&self, args: ExtRequest) -> Result { self.as_ref().ext_method(args).await } diff --git a/src/agent-client-protocol/src/lib.rs b/src/agent-client-protocol/src/lib.rs index 2abaab9..93bb851 100644 --- a/src/agent-client-protocol/src/lib.rs +++ b/src/agent-client-protocol/src/lib.rs @@ -175,6 +175,16 @@ impl Agent for ClientSideConnection { .await } + #[cfg(feature = "unstable_session_resume")] + async fn resume_session(&self, args: ResumeSessionRequest) -> Result { + self.conn + .request( + AGENT_METHOD_NAMES.session_resume, + Some(ClientRequest::ResumeSessionRequest(args)), + ) + .await + } + async fn ext_method(&self, args: ExtRequest) -> Result { self.conn .request( @@ -546,6 +556,10 @@ impl Side for AgentSide { m if m == AGENT_METHOD_NAMES.session_fork => serde_json::from_str(params.get()) .map(ClientRequest::ForkSessionRequest) .map_err(Into::into), + #[cfg(feature = "unstable_session_resume")] + m if m == AGENT_METHOD_NAMES.session_resume => serde_json::from_str(params.get()) + .map(ClientRequest::ResumeSessionRequest) + .map_err(Into::into), m if m == AGENT_METHOD_NAMES.session_prompt => serde_json::from_str(params.get()) .map(ClientRequest::PromptRequest) .map_err(Into::into), @@ -625,6 +639,11 @@ impl MessageHandler for T { let response = self.fork_session(args).await?; Ok(AgentResponse::ForkSessionResponse(response)) } + #[cfg(feature = "unstable_session_resume")] + ClientRequest::ResumeSessionRequest(args) => { + let response = self.resume_session(args).await?; + Ok(AgentResponse::ResumeSessionResponse(response)) + } ClientRequest::ExtMethodRequest(args) => { let response = self.ext_method(args).await?; Ok(AgentResponse::ExtMethodResponse(response)) diff --git a/src/agent-client-protocol/src/rpc_tests.rs b/src/agent-client-protocol/src/rpc_tests.rs index 3e62d96..e7aadf7 100644 --- a/src/agent-client-protocol/src/rpc_tests.rs +++ b/src/agent-client-protocol/src/rpc_tests.rs @@ -240,6 +240,18 @@ impl Agent for TestAgent { )) } + #[cfg(feature = "unstable_session_resume")] + async fn resume_session( + &self, + args: agent_client_protocol_schema::ResumeSessionRequest, + ) -> Result { + // Check if session exists + if !self.sessions.lock().unwrap().contains_key(&args.session_id) { + return Err(Error::invalid_params()); + } + Ok(agent_client_protocol_schema::ResumeSessionResponse::new()) + } + async fn ext_method(&self, args: ExtRequest) -> Result { dbg!(); match dbg!(args.method.as_ref()) { @@ -773,3 +785,37 @@ async fn test_list_sessions() { }) .await; } + +#[cfg(feature = "unstable_session_resume")] +#[tokio::test] +async fn test_resume_session() { + let local_set = tokio::task::LocalSet::new(); + local_set + .run_until(async { + let client = TestClient::new(); + let agent = TestAgent::new(); + + let (agent_conn, _client_conn) = create_connection_pair(&client, &agent); + + // First create a session + let new_session_response = agent_conn + .new_session(NewSessionRequest::new("/test")) + .await + .expect("new_session failed"); + + let session_id = new_session_response.session_id; + + // Resume the session + let resume_response = agent_conn + .resume_session(agent_client_protocol_schema::ResumeSessionRequest::new( + session_id.clone(), + "/test", + )) + .await + .expect("resume_session failed"); + + // Verify we got a valid response (no modes by default in TestAgent) + assert!(resume_response.modes.is_none()); + }) + .await; +}