diff --git a/src/agent-client-protocol/CHANGELOG.md b/src/agent-client-protocol/CHANGELOG.md index aff8de8..4003afe 100644 --- a/src/agent-client-protocol/CHANGELOG.md +++ b/src/agent-client-protocol/CHANGELOG.md @@ -66,6 +66,10 @@ ### Fixed +- Preserve stable v1 `NewSessionResponse::config_options` on `ActiveSession`, + expose them through `ActiveSession::config_options`, and include them in + reconstructed and proxied session responses. + ([#301](https://github.com/agentclientprotocol/rust-sdk/issues/301)) - *(unstable-v2)* Require native v2 client and agent connections to complete their single initialization handshake before sending or accepting other protocol traffic. Reject initialization in the wrong direction and reject diff --git a/src/agent-client-protocol/src/session.rs b/src/agent-client-protocol/src/session.rs index 47200c0..9a3a79e 100644 --- a/src/agent-client-protocol/src/session.rs +++ b/src/agent-client-protocol/src/session.rs @@ -11,8 +11,8 @@ use crate::{ role::{HasPeer, acp::ProxySessionMessages}, schema::v1::{ ContentBlock, ContentChunk, NewSessionRequest, NewSessionResponse, PromptRequest, - PromptResponse, SessionId, SessionModeState, SessionNotification, SessionUpdate, - StopReason, + PromptResponse, SessionConfigOption, SessionId, SessionModeState, SessionNotification, + SessionUpdate, StopReason, }, util::{MatchDispatch, MatchDispatchFrom, run_until}, }; @@ -93,6 +93,7 @@ where let NewSessionResponse { session_id, modes, + config_options, meta, .. } = response; @@ -104,6 +105,7 @@ where Ok(ActiveSession { session_id, modes, + config_options, meta, update_rx, update_tx, @@ -526,6 +528,7 @@ where update_rx: mpsc::UnboundedReceiver, update_tx: mpsc::UnboundedSender, modes: Option, + config_options: Option>, meta: Option>, connection: ConnectionTo, @@ -573,6 +576,11 @@ where self.modes.as_ref() } + /// Access the initial session configuration options returned by the agent. + pub fn config_options(&self) -> Option<&[SessionConfigOption]> { + self.config_options.as_deref() + } + /// Access meta data from session response. pub fn meta(&self) -> Option<&serde_json::Map> { self.meta.as_ref() @@ -585,6 +593,7 @@ where pub fn response(&self) -> NewSessionResponse { NewSessionResponse::new(self.session_id.clone()) .modes(self.modes.clone()) + .config_options(self.config_options.clone()) .meta(self.meta.clone()) } @@ -697,6 +706,7 @@ where mcp_handler_registrations, // These fields are not needed for proxying modes: _, + config_options: _, meta: _, _runner, } = self; diff --git a/src/agent-client-protocol/tests/session_ordering.rs b/src/agent-client-protocol/tests/session_ordering.rs index 53805f9..dee063a 100644 --- a/src/agent-client-protocol/tests/session_ordering.rs +++ b/src/agent-client-protocol/tests/session_ordering.rs @@ -5,7 +5,9 @@ use agent_client_protocol::{ SessionMessage, TransportBatch, TransportFrame, schema::v1::{ ContentBlock, ContentChunk, NewSessionRequest, NewSessionResponse, PromptRequest, - PromptResponse, SessionId, SessionNotification, SessionUpdate, StopReason, TextContent, + PromptResponse, SessionConfigOption, SessionConfigOptionCategory, + SessionConfigSelectOption, SessionId, SessionNotification, SessionUpdate, StopReason, + TextContent, }, }; use futures::{StreamExt as _, channel::oneshot}; @@ -169,6 +171,53 @@ mod callback_future_lifetimes { } } +#[tokio::test(flavor = "current_thread")] +async fn active_session_preserves_config_options_from_new_session_response() { + let config_options = vec![ + SessionConfigOption::select( + "model", + "Model", + "sonnet", + vec![ + SessionConfigSelectOption::new("sonnet", "Sonnet"), + SessionConfigSelectOption::new("opus", "Opus"), + ], + ) + .category(SessionConfigOptionCategory::Model), + ]; + let expected_response = + NewSessionResponse::new("config-options-session").config_options(config_options.clone()); + let agent_response = expected_response.clone(); + + let agent = Agent.builder().on_receive_request( + async move |_request: NewSessionRequest, + responder: Responder, + _connection: ConnectionTo| { + responder.respond(agent_response.clone()) + }, + agent_client_protocol::on_receive_request!(), + ); + + let client = Client + .builder() + .connect_with(agent, async move |connection| { + let session = connection + .build_session_cwd()? + .block_task() + .start_session() + .await?; + + assert_eq!(session.config_options(), Some(config_options.as_slice())); + assert_eq!(session.response(), expected_response); + Ok(()) + }); + + tokio::time::timeout(TIMEOUT, client) + .await + .expect("session setup timed out") + .expect("session connection failed"); +} + #[tokio::test(flavor = "current_thread")] async fn on_session_start_callback_can_consume_later_session_messages() { let session_id = SessionId::new("ordered-session");