镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions agent-client-protocol-schema/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ unstable = [
"unstable_mcp_over_acp",
"unstable_plan_operations",
"unstable_session_fork",
"unstable_session_set_title",
"unstable_session_system_prompt",
"unstable_subagents",
"unstable_end_turn_token_usage",
Expand All @@ -43,6 +44,7 @@ unstable_llm_providers = []
unstable_mcp_over_acp = []
unstable_plan_operations = []
unstable_session_fork = []
unstable_session_set_title = []
unstable_session_system_prompt = []
unstable_subagents = []
unstable_end_turn_token_usage = []
Expand Down
283 changes: 283 additions & 0 deletions agent-client-protocol-schema/src/v1/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1951,6 +1951,129 @@ impl SessionInfo {
}
}

// Set session title

/// **UNSTABLE**
///
/// This capability is not part of the spec yet, and may be removed or changed at any point.
///
/// Request parameters for setting the title of a session.
///
/// Only available if the Agent supports the `sessionCapabilities.title` capability.
///
/// The session does not need to be active on the connection. Any session the Agent would
/// return from `session/list` can be retitled. A session that does not exist gets `-32002`.
///
/// The Agent responds only after it has stored the title. After a successful response, the
/// Agent SHOULD NOT replace the title with one it generates automatically.
#[cfg(feature = "unstable_session_set_title")]
#[serde_as]
#[skip_serializing_none]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SetSessionTitleRequest {
/// The ID of the session to retitle.
pub session_id: SessionId,
/// The new title for the session.
///
/// Required and non-null. Omission and explicit `null` are both invalid. The Agent MAY
/// normalize the title before storing it, for example by trimming whitespace or
/// truncating it. The Agent rejects a title that fails its validation, including one
/// that is empty after normalization, with `-32602`.
pub title: 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.
///
/// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility)
#[serde_as(deserialize_as = "DefaultOnError")]
#[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
#[serde(default)]
#[serde(rename = "_meta")]
pub meta: Option<Meta>,
}

#[cfg(feature = "unstable_session_set_title")]
impl SetSessionTitleRequest {
/// Builds [`SetSessionTitleRequest`] with the required request fields set. Optional fields start unset or empty.
#[must_use]
pub fn new(session_id: impl Into<SessionId>, title: impl Into<String>) -> Self {
Self {
session_id: session_id.into(),
title: title.into(),
meta: None,
}
}

/// 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/v1/extensibility)
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}

/// **UNSTABLE**
///
/// This capability is not part of the spec yet, and may be removed or changed at any point.
///
/// Response from setting the title of a session.
#[cfg(feature = "unstable_session_set_title")]
#[serde_as]
#[skip_serializing_none]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct SetSessionTitleResponse {
/// The title the Agent stored.
///
/// This differs from the requested title if the Agent normalized it. Clients SHOULD
/// display this value rather than the one they sent.
pub title: 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.
///
/// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility)
#[serde_as(deserialize_as = "DefaultOnError")]
#[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
#[serde(default)]
#[serde(rename = "_meta")]
pub meta: Option<Meta>,
}

#[cfg(feature = "unstable_session_set_title")]
impl SetSessionTitleResponse {
/// Builds [`SetSessionTitleResponse`] with the required response fields set. Optional fields start unset or empty.
#[must_use]
pub fn new(title: impl Into<String>) -> Self {
Self {
title: title.into(),
meta: None,
}
}

/// 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/v1/extensibility)
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}

// Session modes

/// The set of modes and the one currently active.
Expand Down Expand Up @@ -4282,6 +4405,22 @@ pub struct SessionCapabilities {
#[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
#[serde(default)]
pub fork: Option<SessionForkCapabilities>,
/// **UNSTABLE**
///
/// This capability is not part of the spec yet, and may be removed or changed at any point.
///
/// Whether the agent supports `session/set_title`.
///
/// Agents report titles through `session/list` and `session_info_update` without this
/// capability. It only advertises that Clients can set titles.
///
/// Optional. Omitted or `null` both mean the agent does not advertise support.
/// Supplying `{}` means the agent supports setting session titles.
#[cfg(feature = "unstable_session_set_title")]
#[serde_as(deserialize_as = "DefaultOnError")]
#[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
#[serde(default)]
pub title: Option<SessionTitleCapabilities>,
/// Whether the agent supports `session/resume`.
///
/// Optional. Omitted or `null` both mean the agent does not advertise support.
Expand Down Expand Up @@ -4384,6 +4523,24 @@ impl SessionCapabilities {
self
}

/// **UNSTABLE**
///
/// This capability is not part of the spec yet, and may be removed or changed at any point.
///
/// Whether the agent supports `session/set_title`.
///
/// Agents report titles through `session/list` and `session_info_update` without this
/// capability. It only advertises that Clients can set titles.
///
/// Omitted or `null` both mean the agent does not advertise support.
/// Supplying `{}` means the agent supports setting session titles.
#[cfg(feature = "unstable_session_set_title")]
#[must_use]
pub fn title(mut self, title: impl IntoOption<SessionTitleCapabilities>) -> Self {
self.title = title.into_option();
self
}

/// Whether the agent supports `session/resume`.
///
/// Omitted or `null` both mean the agent does not advertise support.
Expand Down Expand Up @@ -4767,6 +4924,53 @@ impl SessionForkCapabilities {
}
}

/// **UNSTABLE**
///
/// This capability is not part of the spec yet, and may be removed or changed at any point.
///
/// Capabilities for Client-set session titles.
///
/// Supplying `{}` means the agent supports `session/set_title`. Agents report titles
/// through `session/list` and `session_info_update` without this capability.
#[cfg(feature = "unstable_session_set_title")]
#[serde_as]
#[skip_serializing_none]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct SessionTitleCapabilities {
/// 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/v1/extensibility)
#[serde_as(deserialize_as = "DefaultOnError")]
#[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
#[serde(default)]
#[serde(rename = "_meta")]
pub meta: Option<Meta>,
}

#[cfg(feature = "unstable_session_set_title")]
impl SessionTitleCapabilities {
/// Builds an empty [`SessionTitleCapabilities`]. Use builder methods to advertise supported sub-capabilities.
#[must_use]
pub fn new() -> Self {
Self::default()
}

/// 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/v1/extensibility)
#[must_use]
pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
self.meta = meta.into_option();
self
}
}

/// Capabilities for the `session/resume` method.
///
/// Supplying `{}` means the agent supports resuming sessions.
Expand Down Expand Up @@ -5067,6 +5271,9 @@ pub struct AgentMethodNames {
/// Method for forking an existing session.
#[cfg(feature = "unstable_session_fork")]
pub session_fork: &'static str,
/// Method for setting the title of a session.
#[cfg(feature = "unstable_session_set_title")]
pub session_set_title: &'static str,
/// Method for resuming an existing session.
pub session_resume: &'static str,
/// Method for closing an active session.
Expand Down Expand Up @@ -5097,6 +5304,8 @@ pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
session_delete: SESSION_DELETE_METHOD_NAME,
#[cfg(feature = "unstable_session_fork")]
session_fork: SESSION_FORK_METHOD_NAME,
#[cfg(feature = "unstable_session_set_title")]
session_set_title: SESSION_SET_TITLE_METHOD_NAME,
session_resume: SESSION_RESUME_METHOD_NAME,
session_close: SESSION_CLOSE_METHOD_NAME,
logout: LOGOUT_METHOD_NAME,
Expand Down Expand Up @@ -5134,6 +5343,9 @@ pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
/// Method name for forking an existing session.
#[cfg(feature = "unstable_session_fork")]
pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
/// Method name for setting the title of a session.
#[cfg(feature = "unstable_session_set_title")]
pub(crate) const SESSION_SET_TITLE_METHOD_NAME: &str = "session/set_title";
/// Method name for resuming an existing session.
pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
/// Method name for closing an active session.
Expand Down Expand Up @@ -5253,6 +5465,18 @@ pub enum ClientRequest {
/// set up with the request's `cwd`, `additionalDirectories`, and `mcpServers`.
/// The client can prompt it without calling `session/load` or `session/resume` first.
ForkSessionRequest(ForkSessionRequest),
#[cfg(feature = "unstable_session_set_title")]
/// **UNSTABLE**
///
/// This capability is not part of the spec yet, and may be removed or changed at any point.
///
/// Sets the title of a session.
///
/// This method is only available if the agent advertises the `sessionCapabilities.title` capability.
///
/// The session does not need to be active on the connection. The agent stores the
/// title and returns the stored title, which may be normalized.
SetSessionTitleRequest(SetSessionTitleRequest),
/// Resumes an existing session without returning previous messages.
///
/// This method is only available if the agent advertises the `sessionCapabilities.resume` capability.
Expand Down Expand Up @@ -5324,6 +5548,8 @@ impl ClientRequest {
Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
#[cfg(feature = "unstable_session_fork")]
Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
#[cfg(feature = "unstable_session_set_title")]
Self::SetSessionTitleRequest(_) => AGENT_METHOD_NAMES.session_set_title,
Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
Self::SetSessionModeRequest(_) => AGENT_METHOD_NAMES.session_set_mode,
Expand Down Expand Up @@ -5373,6 +5599,9 @@ pub enum AgentResponse {
/// Successful result returned for a `session/fork` request.
#[cfg(feature = "unstable_session_fork")]
ForkSessionResponse(ForkSessionResponse),
/// Successful result returned for a `session/set_title` request.
#[cfg(feature = "unstable_session_set_title")]
SetSessionTitleResponse(SetSessionTitleResponse),
/// Successful result returned for a `session/resume` request.
ResumeSessionResponse(#[serde(default)] ResumeSessionResponse),
/// Successful result returned for a `session/close` request.
Expand Down Expand Up @@ -5961,6 +6190,60 @@ mod test_serialization {
})
);
}
#[cfg(feature = "unstable_session_set_title")]
#[test]
fn test_session_set_title_serialization() {
assert_eq!(AGENT_METHOD_NAMES.session_set_title, "session/set_title");
assert_eq!(
ClientRequest::SetSessionTitleRequest(SetSessionTitleRequest::new(
"sess_abc123",
"Investigate OAuth timeout"
))
.method(),
"session/set_title"
);
assert_eq!(
serde_json::to_value(SetSessionTitleRequest::new(
"sess_abc123",
"Investigate OAuth timeout"
))
.unwrap(),
json!({
"sessionId": "sess_abc123",
"title": "Investigate OAuth timeout"
})
);
assert_eq!(
serde_json::to_value(SetSessionTitleResponse::new("Investigate OAuth timeout"))
.unwrap(),
json!({
"title": "Investigate OAuth timeout"
})
);
assert_eq!(
serde_json::to_value(SessionCapabilities::new().title(SessionTitleCapabilities::new()))
.unwrap(),
json!({
"title": {}
})
);
}

#[cfg(feature = "unstable_session_set_title")]
#[test]
fn test_session_set_title_requires_title() {
for params in [
json!({"sessionId": "sess_abc123"}),
json!({"sessionId": "sess_abc123", "title": null}),
json!({"sessionId": "sess_abc123", "title": 42}),
] {
assert!(serde_json::from_value::<SetSessionTitleRequest>(params).is_err());
}
for result in [json!({}), json!({"title": null})] {
assert!(serde_json::from_value::<SetSessionTitleResponse>(result).is_err());
}
}

#[test]
fn test_session_additional_directories_serialization() {
assert_eq!(
Expand Down
Loading
Loading