Skip to content

Commit 0541b81

Browse files
authored
fix(acp): Preserve session config options (#332)
Closes #301
1 parent 832dbec commit 0541b81

3 files changed

Lines changed: 66 additions & 3 deletions

File tree

‎src/agent-client-protocol/CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@
6666

6767
### Fixed
6868

69+
- Preserve stable v1 `NewSessionResponse::config_options` on `ActiveSession`,
70+
expose them through `ActiveSession::config_options`, and include them in
71+
reconstructed and proxied session responses.
72+
([#301](https://github.com/agentclientprotocol/rust-sdk/issues/301))
6973
- *(unstable-v2)* Require native v2 client and agent connections to complete
7074
their single initialization handshake before sending or accepting other
7175
protocol traffic. Reject initialization in the wrong direction and reject

‎src/agent-client-protocol/src/session.rs‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use crate::{
1111
role::{HasPeer, acp::ProxySessionMessages},
1212
schema::v1::{
1313
ContentBlock, ContentChunk, NewSessionRequest, NewSessionResponse, PromptRequest,
14-
PromptResponse, SessionId, SessionModeState, SessionNotification, SessionUpdate,
15-
StopReason,
14+
PromptResponse, SessionConfigOption, SessionId, SessionModeState, SessionNotification,
15+
SessionUpdate, StopReason,
1616
},
1717
util::{MatchDispatch, MatchDispatchFrom, run_until},
1818
};
@@ -93,6 +93,7 @@ where
9393
let NewSessionResponse {
9494
session_id,
9595
modes,
96+
config_options,
9697
meta,
9798
..
9899
} = response;
@@ -104,6 +105,7 @@ where
104105
Ok(ActiveSession {
105106
session_id,
106107
modes,
108+
config_options,
107109
meta,
108110
update_rx,
109111
update_tx,
@@ -526,6 +528,7 @@ where
526528
update_rx: mpsc::UnboundedReceiver<SessionMessage>,
527529
update_tx: mpsc::UnboundedSender<SessionMessage>,
528530
modes: Option<SessionModeState>,
531+
config_options: Option<Vec<SessionConfigOption>>,
529532
meta: Option<serde_json::Map<String, serde_json::Value>>,
530533
connection: ConnectionTo<Link>,
531534

@@ -573,6 +576,11 @@ where
573576
self.modes.as_ref()
574577
}
575578

579+
/// Access the initial session configuration options returned by the agent.
580+
pub fn config_options(&self) -> Option<&[SessionConfigOption]> {
581+
self.config_options.as_deref()
582+
}
583+
576584
/// Access meta data from session response.
577585
pub fn meta(&self) -> Option<&serde_json::Map<String, serde_json::Value>> {
578586
self.meta.as_ref()
@@ -585,6 +593,7 @@ where
585593
pub fn response(&self) -> NewSessionResponse {
586594
NewSessionResponse::new(self.session_id.clone())
587595
.modes(self.modes.clone())
596+
.config_options(self.config_options.clone())
588597
.meta(self.meta.clone())
589598
}
590599

@@ -697,6 +706,7 @@ where
697706
mcp_handler_registrations,
698707
// These fields are not needed for proxying
699708
modes: _,
709+
config_options: _,
700710
meta: _,
701711
_runner,
702712
} = self;

‎src/agent-client-protocol/tests/session_ordering.rs‎

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use agent_client_protocol::{
55
SessionMessage, TransportBatch, TransportFrame,
66
schema::v1::{
77
ContentBlock, ContentChunk, NewSessionRequest, NewSessionResponse, PromptRequest,
8-
PromptResponse, SessionId, SessionNotification, SessionUpdate, StopReason, TextContent,
8+
PromptResponse, SessionConfigOption, SessionConfigOptionCategory,
9+
SessionConfigSelectOption, SessionId, SessionNotification, SessionUpdate, StopReason,
10+
TextContent,
911
},
1012
};
1113
use futures::{StreamExt as _, channel::oneshot};
@@ -169,6 +171,53 @@ mod callback_future_lifetimes {
169171
}
170172
}
171173

174+
#[tokio::test(flavor = "current_thread")]
175+
async fn active_session_preserves_config_options_from_new_session_response() {
176+
let config_options = vec![
177+
SessionConfigOption::select(
178+
"model",
179+
"Model",
180+
"sonnet",
181+
vec![
182+
SessionConfigSelectOption::new("sonnet", "Sonnet"),
183+
SessionConfigSelectOption::new("opus", "Opus"),
184+
],
185+
)
186+
.category(SessionConfigOptionCategory::Model),
187+
];
188+
let expected_response =
189+
NewSessionResponse::new("config-options-session").config_options(config_options.clone());
190+
let agent_response = expected_response.clone();
191+
192+
let agent = Agent.builder().on_receive_request(
193+
async move |_request: NewSessionRequest,
194+
responder: Responder<NewSessionResponse>,
195+
_connection: ConnectionTo<Client>| {
196+
responder.respond(agent_response.clone())
197+
},
198+
agent_client_protocol::on_receive_request!(),
199+
);
200+
201+
let client = Client
202+
.builder()
203+
.connect_with(agent, async move |connection| {
204+
let session = connection
205+
.build_session_cwd()?
206+
.block_task()
207+
.start_session()
208+
.await?;
209+
210+
assert_eq!(session.config_options(), Some(config_options.as_slice()));
211+
assert_eq!(session.response(), expected_response);
212+
Ok(())
213+
});
214+
215+
tokio::time::timeout(TIMEOUT, client)
216+
.await
217+
.expect("session setup timed out")
218+
.expect("session connection failed");
219+
}
220+
172221
#[tokio::test(flavor = "current_thread")]
173222
async fn on_session_start_callback_can_consume_later_session_messages() {
174223
let session_id = SessionId::new("ordered-session");

0 commit comments

Comments
 (0)