Skip to content

Commit 33de434

Browse files
feat(acp): route session/setTitle to current schema types
1 parent a9affa6 commit 33de434

11 files changed

Lines changed: 155 additions & 93 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ agent-client-protocol-trace-viewer = { path = "src/agent-client-protocol-trace-v
3737
yopo = { package = "agent-client-protocol-yopo", path = "src/yopo" }
3838

3939
# Protocol
40-
agent-client-protocol-schema = { version = "=1.1.0", features = ["tracing"] }
40+
agent-client-protocol-schema = { version = "=1.2.0", features = ["tracing"] }
4141

4242
# Core async runtime
4343
tokio = { version = "1.52", default-features = false }
@@ -113,3 +113,7 @@ struct_field_names = "allow"
113113
too_many_lines = "allow"
114114
type_complexity = "allow"
115115
wildcard_imports = "allow"
116+
117+
[patch.crates-io]
118+
# Temporary: consume session/setTitle schema types before they are released.
119+
agent-client-protocol-schema = { git = "https://github.com/daniel-agentee/agent-client-protocol", rev = "064ee1ffe194cdb14e1d45d66875ce74a14933c3" }

src/agent-client-protocol-test/src/testy.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use agent_client_protocol::schema::v1::{
3131
CompleteElicitationNotification, CreateElicitationRequest, ElicitationAction,
3232
ElicitationCapabilities, ElicitationFormMode, ElicitationRequestScope, ElicitationSchema,
3333
ElicitationSessionScope, ElicitationUrlMode, ErrorCode, MultiSelectPropertySchema, RequestId,
34-
StringPropertySchema, UrlElicitationRequiredData, UrlElicitationRequiredItem,
34+
StringPropertySchema,
3535
};
3636
use agent_client_protocol::{
3737
Agent, Client, ConnectTo, ConnectionTo, JsonRpcRequest, Responder, SentRequest,
@@ -637,7 +637,7 @@ impl Testy {
637637
Err(error) => {
638638
self.finish_prompt(&session_id, StopReason::EndTurn);
639639
#[cfg(feature = "unstable")]
640-
if error.code == ErrorCode::UrlElicitationRequired {
640+
if is_url_elicitation_required_error(&error) {
641641
return responder.respond_with_error(error);
642642
}
643643
return Err(error);
@@ -1765,15 +1765,28 @@ fn elicitation_cancelled(agent: &Testy, session_id: &SessionId, report: &mut Vec
17651765

17661766
#[cfg(feature = "unstable")]
17671767
fn url_elicitation_required_error() -> agent_client_protocol::Error {
1768-
let data = UrlElicitationRequiredData::new(vec![UrlElicitationRequiredItem::new(
1769-
"testy-url-required",
1770-
"https://example.com/testy/required",
1771-
"Complete the Testy URL elicitation before continuing",
1772-
)]);
1773-
match serde_json::to_value(data) {
1774-
Ok(data) => agent_client_protocol::Error::url_elicitation_required().data(data),
1775-
Err(error) => agent_client_protocol::Error::into_internal_error(error),
1776-
}
1768+
agent_client_protocol::Error::invalid_params().data(url_elicitation_required_data())
1769+
}
1770+
1771+
#[cfg(feature = "unstable")]
1772+
fn is_url_elicitation_required_error(error: &agent_client_protocol::Error) -> bool {
1773+
error.code == ErrorCode::InvalidParams
1774+
&& error
1775+
.data
1776+
.as_ref()
1777+
.is_some_and(|data| data.get("elicitations").is_some())
1778+
}
1779+
1780+
#[cfg(feature = "unstable")]
1781+
fn url_elicitation_required_data() -> serde_json::Value {
1782+
serde_json::json!({
1783+
"elicitations": [{
1784+
"mode": "url",
1785+
"elicitationId": "testy-url-required",
1786+
"url": "https://example.com/testy/required",
1787+
"message": "Complete the Testy URL elicitation before continuing"
1788+
}]
1789+
})
17771790
}
17781791

17791792
fn send_session_update(

src/agent-client-protocol-test/tests/testy.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use agent_client_protocol::schema::v1::{
1212
CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse,
1313
ElicitationAcceptAction, ElicitationAction, ElicitationCapabilities, ElicitationContentValue,
1414
ElicitationFormCapabilities, ElicitationMode, ElicitationScope, ElicitationUrlCapabilities,
15-
ErrorCode, UrlElicitationRequiredData,
15+
ErrorCode,
1616
};
1717
use agent_client_protocol::{
1818
Client, Responder,
@@ -1958,18 +1958,18 @@ async fn testy_callbacks_with_unstable_feature_returns_url_required_when_url_eli
19581958
.block_task()
19591959
.await
19601960
.expect_err("url-required elicitation scenario should fail the prompt request");
1961-
assert_eq!(error.code, ErrorCode::UrlElicitationRequired);
1962-
1963-
let data: UrlElicitationRequiredData =
1964-
serde_json::from_value(error.data.expect("url-required error should include data"))
1965-
.expect("url-required error data should deserialize");
1966-
assert_eq!(data.elicitations.len(), 1);
1967-
let elicitation = &data.elicitations[0];
1968-
assert_eq!(elicitation.elicitation_id.to_string(), "testy-url-required");
1969-
assert_eq!(elicitation.url, "https://example.com/testy/required");
1961+
assert_eq!(error.code, ErrorCode::InvalidParams);
1962+
19701963
assert_eq!(
1971-
elicitation.message,
1972-
"Complete the Testy URL elicitation before continuing"
1964+
error.data.expect("url-required error should include data"),
1965+
serde_json::json!({
1966+
"elicitations": [{
1967+
"mode": "url",
1968+
"elicitationId": "testy-url-required",
1969+
"url": "https://example.com/testy/required",
1970+
"message": "Complete the Testy URL elicitation before continuing"
1971+
}]
1972+
})
19731973
);
19741974
Ok(())
19751975
})

src/agent-client-protocol/src/capabilities.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ mod tests {
142142

143143
#[test]
144144
fn test_add_capability_to_request() {
145-
let request = InitializeRequest::new(ProtocolVersion::LATEST);
145+
let request = InitializeRequest::new(ProtocolVersion::V1);
146146

147147
let request = request.add_meta_capability(TestCapability);
148148

@@ -165,8 +165,8 @@ mod tests {
165165
);
166166
let client_capabilities = ClientCapabilities::new().meta(meta);
167167

168-
let request = InitializeRequest::new(ProtocolVersion::LATEST)
169-
.client_capabilities(client_capabilities);
168+
let request =
169+
InitializeRequest::new(ProtocolVersion::V1).client_capabilities(client_capabilities);
170170

171171
let request = request.remove_meta_capability(TestCapability);
172172

@@ -175,7 +175,7 @@ mod tests {
175175

176176
#[test]
177177
fn test_add_capability_to_response() {
178-
let response = InitializeResponse::new(ProtocolVersion::LATEST);
178+
let response = InitializeResponse::new(ProtocolVersion::V1);
179179

180180
let response = response.add_meta_capability(TestCapability);
181181

@@ -198,8 +198,8 @@ mod tests {
198198
);
199199
let client_capabilities = ClientCapabilities::new().meta(meta);
200200

201-
let request = InitializeRequest::new(ProtocolVersion::LATEST)
202-
.client_capabilities(client_capabilities);
201+
let request =
202+
InitializeRequest::new(ProtocolVersion::V1).client_capabilities(client_capabilities);
203203

204204
assert!(!request.has_meta_capability(TestCapability));
205205
}
@@ -216,8 +216,8 @@ mod tests {
216216
);
217217
let client_capabilities = ClientCapabilities::new().meta(meta);
218218

219-
let request = InitializeRequest::new(ProtocolVersion::LATEST)
220-
.client_capabilities(client_capabilities);
219+
let request =
220+
InitializeRequest::new(ProtocolVersion::V1).client_capabilities(client_capabilities);
221221

222222
assert!(!request.has_meta_capability(TestCapability));
223223
}

src/agent-client-protocol/src/jsonrpc/protocol_compat.rs

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,11 @@ mod imp {
536536
}
537537

538538
fn public_to_v2_message(message: UntypedMessage) -> Result<UntypedMessage, crate::Error> {
539-
let UntypedMessage { method, params } = message;
539+
let UntypedMessage { method, mut params } = message;
540+
541+
if method == "initialize" {
542+
ensure_legacy_initialize_info(&mut params, "clientInfo");
543+
}
540544

541545
if let Some(message) = try_convert_message_to_v2::<ClientRequest>(&method, &params)? {
542546
return Ok(message);
@@ -573,8 +577,12 @@ mod imp {
573577

574578
fn public_to_v2_response(
575579
method: &str,
576-
value: serde_json::Value,
580+
mut value: serde_json::Value,
577581
) -> Result<serde_json::Value, crate::Error> {
582+
if method == "initialize" {
583+
ensure_legacy_initialize_info(&mut value, "agentInfo");
584+
}
585+
578586
if let Some(value) = try_convert_response_to_v2::<AgentResponse>(method, &value)? {
579587
return Ok(value);
580588
}
@@ -585,6 +593,27 @@ mod imp {
585593
Ok(value)
586594
}
587595

596+
fn ensure_legacy_initialize_info(value: &mut serde_json::Value, field: &str) {
597+
let serde_json::Value::Object(object) = value else {
598+
return;
599+
};
600+
601+
let needs_default = match object.get(field) {
602+
Some(value) => value.is_null(),
603+
None => true,
604+
};
605+
606+
if needs_default {
607+
object.insert(
608+
field.into(),
609+
serde_json::json!({
610+
"name": "legacy-acp-peer",
611+
"version": "unknown",
612+
}),
613+
);
614+
}
615+
}
616+
588617
fn v2_to_public_response(
589618
method: &str,
590619
value: serde_json::Value,
@@ -728,6 +757,10 @@ mod imp {
728757
.negotiated
729758
}
730759

760+
fn v2_implementation() -> v2::Implementation {
761+
v2::Implementation::new("rust-sdk-test", "0.0.0")
762+
}
763+
731764
#[test]
732765
fn initialize_request_sets_active_wire_version_before_response() -> Result<(), crate::Error>
733766
{
@@ -736,7 +769,7 @@ mod imp {
736769

737770
compat.incoming_message(UntypedMessage::new(
738771
"initialize",
739-
v2::InitializeRequest::new(ProtocolVersion::V2),
772+
v2::InitializeRequest::new(ProtocolVersion::V2, v2_implementation()),
740773
)?)?;
741774

742775
assert_eq!(negotiated(&compat), ProtocolVersionKind::V1);
@@ -746,6 +779,7 @@ mod imp {
746779
"initialize",
747780
Ok(serde_json::to_value(v2::InitializeResponse::new(
748781
ProtocolVersion::V2,
782+
v2_implementation(),
749783
))?),
750784
)?;
751785

@@ -762,7 +796,7 @@ mod imp {
762796

763797
compat.outgoing_message(UntypedMessage::new(
764798
"initialize",
765-
v2::InitializeRequest::new(ProtocolVersion::V1),
799+
v2::InitializeRequest::new(ProtocolVersion::V1, v2_implementation()),
766800
)?)?;
767801

768802
assert_eq!(negotiated(&compat), ProtocolVersionKind::V1);
@@ -772,6 +806,7 @@ mod imp {
772806
"initialize",
773807
Ok(serde_json::to_value(v2::InitializeResponse::new(
774808
ProtocolVersion::V2,
809+
v2_implementation(),
775810
))?),
776811
)?;
777812

@@ -788,7 +823,7 @@ mod imp {
788823

789824
compat.outgoing_message(UntypedMessage::new(
790825
"initialize",
791-
v2::InitializeRequest::new(ProtocolVersion::V1),
826+
v2::InitializeRequest::new(ProtocolVersion::V1, v2_implementation()),
792827
)?)?;
793828

794829
assert_eq!(negotiated(&compat), ProtocolVersionKind::V1);
@@ -814,7 +849,7 @@ mod imp {
814849
let compat = ProtocolCompat::new(ProtocolMode::v2_client());
815850
compat.outgoing_message(UntypedMessage::new(
816851
"initialize",
817-
v2::InitializeRequest::new(ProtocolVersion::V1),
852+
v2::InitializeRequest::new(ProtocolVersion::V1, v2_implementation()),
818853
)?)?;
819854

820855
let error = compat
@@ -838,7 +873,7 @@ mod imp {
838873
let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
839874
let messages = compat.outgoing_notification(UntypedMessage::new(
840875
"session/update",
841-
v2::SessionNotification::new(
876+
v2::UpdateSessionNotification::new(
842877
"sess",
843878
v2::SessionUpdate::AgentMessage(v2::AgentMessage::new("msg_agent").content(
844879
vec![

src/agent-client-protocol/src/schema/client_to_agent/requests.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::schema::v1::{
55
LogoutRequest, LogoutResponse, NewSessionRequest, NewSessionResponse, PromptRequest,
66
PromptResponse, ResumeSessionRequest, ResumeSessionResponse, SetSessionConfigOptionRequest,
77
SetSessionConfigOptionResponse, SetSessionModeRequest, SetSessionModeResponse,
8+
SetSessionTitleRequest, SetSessionTitleResponse,
89
};
910
#[cfg(feature = "unstable_session_fork")]
1011
use crate::schema::v1::{ForkSessionRequest, ForkSessionResponse};
@@ -31,6 +32,11 @@ impl_jsonrpc_request!(
3132
SetSessionConfigOptionResponse,
3233
"session/set_config_option"
3334
);
35+
impl_jsonrpc_request!(
36+
SetSessionTitleRequest,
37+
SetSessionTitleResponse,
38+
"session/setTitle"
39+
);
3440

3541
#[cfg(feature = "unstable_session_fork")]
3642
impl_jsonrpc_request!(ForkSessionRequest, ForkSessionResponse, "session/fork");

src/agent-client-protocol/src/schema/enum_impls.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ impl_jsonrpc_request_enum!(ClientRequest {
2424
CloseSessionRequest => "session/close",
2525
SetSessionModeRequest => "session/set_mode",
2626
SetSessionConfigOptionRequest => "session/set_config_option",
27+
SetSessionTitleRequest => "session/setTitle",
2728
PromptRequest => "session/prompt",
2829
#[cfg(feature = "unstable_mcp_over_acp")]
2930
MessageMcpRequest => "mcp/message",

0 commit comments

Comments
 (0)