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
105 changes: 105 additions & 0 deletions docs/protocol/schema.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,12 @@ See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol

**Properties:**

<ResponseField name="modes" type={<><span><a href="#sessionmodestate">SessionModeState</a></span><span> | null</span></>} >
**UNSTABLE**

This field is not part of the spec, and may be removed or changed at any point.

</ResponseField>
<ResponseField name="sessionId" type={<a href="#sessionid">SessionId</a>} required>
Unique identifier for the created session.

Expand Down Expand Up @@ -981,6 +987,21 @@ An image provided to or from an LLM.
<ResponseField name="mimeType" type={"string"} required></ResponseField>
<ResponseField name="uri" type={"string | null"}></ResponseField>

## <span class="font-mono">LoadSessionResponse</span>

Response from loading an existing session.

**Type:** Object

**Properties:**

<ResponseField name="modes" type={<><span><a href="#sessionmodestate">SessionModeState</a></span><span> | null</span></>} >
**UNSTABLE**

This field is not part of the spec, and may be removed or changed at any point.

</ResponseField>

## <span class="font-mono">McpCapabilities</span>

MCP capabilities supported by the agent
Expand Down Expand Up @@ -1431,6 +1452,60 @@ See protocol docs: [Session ID](https://agentclientprotocol.com/protocol/session

**Type:** `string`

## <span class="font-mono">SessionMode</span>

**UNSTABLE**

This type is not part of the spec, and may be removed or changed at any point.

**Type:** Object

**Properties:**

<ResponseField name="description" type={"string | null"}></ResponseField>
<ResponseField
name="id"
type={<a href="#sessionmodeid">SessionModeId</a>}
required
></ResponseField>
<ResponseField name="name" type={"string"} required></ResponseField>

## <span class="font-mono">SessionModeId</span>

**UNSTABLE**

This type is not part of the spec, and may be removed or changed at any point.

**Type:** `string`

## <span class="font-mono">SessionModeState</span>

**UNSTABLE**

This type is not part of the spec, and may be removed or changed at any point.

**Type:** Object

**Properties:**

<ResponseField
name="availableModes"
type={
<>
<span>
<a href="#sessionmode">SessionMode</a>
</span>
<span>[]</span>
</>
}
required
></ResponseField>
<ResponseField
name="currentModeId"
type={<a href="#sessionmodeid">SessionModeId</a>}
required
></ResponseField>

## <span class="font-mono">SessionUpdate</span>

Different types of updates that can be sent during session processing.
Expand Down Expand Up @@ -1648,6 +1723,29 @@ Available commands are ready or have changed
</Expandable>
</ResponseField>

<ResponseField name="current_mode_update">
The current mode of the session has changed

<Expandable title="Properties">

<ResponseField
name="currentModeId"
type={<a href="#sessionmodeid">SessionModeId</a>}
required
></ResponseField>
<ResponseField name="sessionUpdate" type={"string"} required></ResponseField>

</Expandable>
</ResponseField>

## <span class="font-mono">SetSessionModeResponse</span>

**UNSTABLE**

This type is not part of the spec, and may be removed or changed at any point.

**Type:** `object`

## <span class="font-mono">StopReason</span>

Reasons why an agent stops processing a prompt turn.
Expand Down Expand Up @@ -1984,4 +2082,11 @@ See protocol docs: [Creating](https://agentclientprotocol.com/protocol/tool-call

<ResponseField name="fetch">Retrieving external data.</ResponseField>

<ResponseField name="switch_mode">
**UNSTABLE**

This tool kind is not part of the spec and may be removed at any point.

</ResponseField>

<ResponseField name="other">Other tool types (default).</ResponseField>
31 changes: 28 additions & 3 deletions rust/acp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,10 @@ impl Agent for ClientSideConnection {
.await
}

async fn load_session(&self, arguments: LoadSessionRequest) -> Result<(), Error> {
async fn load_session(
&self,
arguments: LoadSessionRequest,
) -> Result<LoadSessionResponse, Error> {
self.conn
.request(
SESSION_LOAD_METHOD_NAME,
Expand All @@ -201,6 +204,19 @@ impl Agent for ClientSideConnection {
.await
}

#[cfg(feature = "unstable")]
async fn set_session_mode(
&self,
arguments: SetSessionModeRequest,
) -> Result<SetSessionModeResponse, Error> {
self.conn
.request(
SESSION_SET_MODE_METHOD_NAME,
Some(ClientRequest::SetSessionModeRequest(arguments)),
)
.await
}

async fn prompt(&self, arguments: PromptRequest) -> Result<PromptResponse, Error> {
self.conn
.request(
Expand Down Expand Up @@ -525,6 +541,10 @@ impl Side for AgentSide {
SESSION_LOAD_METHOD_NAME => serde_json::from_str(params.get())
.map(ClientRequest::LoadSessionRequest)
.map_err(Into::into),
#[cfg(feature = "unstable")]
SESSION_SET_MODE_METHOD_NAME => serde_json::from_str(params.get())
.map(ClientRequest::SetSessionModeRequest)
.map_err(Into::into),
SESSION_PROMPT_METHOD_NAME => serde_json::from_str(params.get())
.map(ClientRequest::PromptRequest)
.map_err(Into::into),
Expand Down Expand Up @@ -563,13 +583,18 @@ impl<T: Agent> MessageHandler<AgentSide> for T {
Ok(AgentResponse::NewSessionResponse(response))
}
ClientRequest::LoadSessionRequest(args) => {
self.load_session(args).await?;
Ok(AgentResponse::LoadSessionResponse)
let response = self.load_session(args).await?;
Ok(AgentResponse::LoadSessionResponse(response))
}
ClientRequest::PromptRequest(args) => {
let response = self.prompt(args).await?;
Ok(AgentResponse::PromptResponse(response))
}
#[cfg(feature = "unstable")]
ClientRequest::SetSessionModeRequest(args) => {
let response = self.set_session_mode(args).await?;
Ok(AgentResponse::SetSessionModeResponse(response))
}
}
}

Expand Down
96 changes: 94 additions & 2 deletions rust/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,16 @@ pub trait Agent {
fn load_session(
&self,
arguments: LoadSessionRequest,
) -> impl Future<Output = Result<(), Error>>;
) -> impl Future<Output = Result<LoadSessionResponse, Error>>;

/// **UNSTABLE**
///
/// This method is not part of the spec, and may be removed or changed at any point.
#[cfg(feature = "unstable")]
fn set_session_mode(
&self,
arguments: SetSessionModeRequest,
) -> impl Future<Output = Result<SetSessionModeResponse, Error>>;

/// Processes a user prompt within a session.
///
Expand Down Expand Up @@ -204,6 +213,11 @@ pub struct NewSessionResponse {
///
/// Used in all subsequent requests for this conversation.
pub session_id: SessionId,
/// **UNSTABLE**
///
/// This field is not part of the spec, and may be removed or changed at any point.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub modes: Option<SessionModeState>,
}

// Load session
Expand All @@ -225,6 +239,72 @@ pub struct LoadSessionRequest {
pub session_id: SessionId,
}

/// Response from loading an existing session.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct LoadSessionResponse {
/// **UNSTABLE**
///
/// This field is not part of the spec, and may be removed or changed at any point.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub modes: Option<SessionModeState>,
}

// Session modes

/// **UNSTABLE**
///
/// This type is not part of the spec, and may be removed or changed at any point.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SessionModeState {
pub current_mode_id: SessionModeId,
pub available_modes: Vec<SessionMode>,
}

/// **UNSTABLE**
///
/// This type is not part of the spec, and may be removed or changed at any point.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SessionMode {
pub id: SessionModeId,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}

/// **UNSTABLE**
///
/// This type is not part of the spec, and may be removed or changed at any point.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct SessionModeId(pub Arc<str>);

impl std::fmt::Display for SessionModeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

/// **UNSTABLE**
///
/// This type is not part of the spec, and may be removed or changed at any point.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[schemars(extend("x-docs-ignore" = true))]
#[serde(rename_all = "camelCase")]
pub struct SetSessionModeRequest {
pub session_id: SessionId,
pub mode_id: SessionModeId,
}

/// **UNSTABLE**
///
/// This type is not part of the spec, and may be removed or changed at any point.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SetSessionModeResponse {}

// MCP

/// Configuration for connecting to an MCP (Model Context Protocol) server.
Expand Down Expand Up @@ -440,6 +520,9 @@ pub struct AgentMethodNames {
pub session_new: &'static str,
/// Method for loading an existing session.
pub session_load: &'static str,
/// Method for setting the mode for a session.
#[cfg(feature = "unstable")]
pub session_set_mode: &'static str,
/// Method for sending a prompt to the agent.
pub session_prompt: &'static str,
/// Notification for cancelling operations.
Expand All @@ -452,6 +535,8 @@ pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
authenticate: AUTHENTICATE_METHOD_NAME,
session_new: SESSION_NEW_METHOD_NAME,
session_load: SESSION_LOAD_METHOD_NAME,
#[cfg(feature = "unstable")]
session_set_mode: SESSION_SET_MODE_METHOD_NAME,
session_prompt: SESSION_PROMPT_METHOD_NAME,
session_cancel: SESSION_CANCEL_METHOD_NAME,
};
Expand All @@ -464,6 +549,9 @@ pub(crate) const AUTHENTICATE_METHOD_NAME: &str = "authenticate";
pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
/// Method name for loading an existing session.
pub(crate) const SESSION_LOAD_METHOD_NAME: &str = "session/load";
/// Method name for setting the mode for a session.
#[cfg(feature = "unstable")]
pub(crate) const SESSION_SET_MODE_METHOD_NAME: &str = "session/set_mode";
/// Method name for sending a prompt.
pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
/// Method name for the cancel notification.
Expand All @@ -483,6 +571,8 @@ pub enum ClientRequest {
AuthenticateRequest(AuthenticateRequest),
NewSessionRequest(NewSessionRequest),
LoadSessionRequest(LoadSessionRequest),
#[cfg(feature = "unstable")]
SetSessionModeRequest(SetSessionModeRequest),
PromptRequest(PromptRequest),
}

Expand All @@ -499,7 +589,9 @@ pub enum AgentResponse {
InitializeResponse(InitializeResponse),
AuthenticateResponse,
NewSessionResponse(NewSessionResponse),
LoadSessionResponse,
LoadSessionResponse(LoadSessionResponse),
#[cfg(feature = "unstable")]
SetSessionModeResponse(SetSessionModeResponse),
PromptResponse(PromptResponse),
}

Expand Down
7 changes: 7 additions & 0 deletions rust/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use anyhow::Result;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[cfg(feature = "unstable")]
use crate::SessionModeId;
use crate::{ContentBlock, Error, Plan, SessionId, ToolCall, ToolCallUpdate};

/// Defines the interface that ACP-compliant clients must implement.
Expand Down Expand Up @@ -165,6 +167,11 @@ pub enum SessionUpdate {
AvailableCommandsUpdate {
available_commands: Vec<AvailableCommand>,
},
/// The current mode of the session has changed
#[cfg(feature = "unstable")]
#[serde(rename_all = "camelCase")]
#[schemars(extend("x-docs-ignore" = true))]
CurrentModeUpdate { current_mode_id: SessionModeId },
}

/// Information about a command.
Expand Down
8 changes: 6 additions & 2 deletions rust/example_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,16 @@ impl acp::Agent for ExampleAgent {
self.next_session_id.set(session_id + 1);
Ok(acp::NewSessionResponse {
session_id: acp::SessionId(session_id.to_string().into()),
modes: None,
})
}

async fn load_session(&self, arguments: acp::LoadSessionRequest) -> Result<(), acp::Error> {
async fn load_session(
&self,
arguments: acp::LoadSessionRequest,
) -> Result<acp::LoadSessionResponse, acp::Error> {
log::info!("Received load session request {arguments:?}");
Err(acp::Error::method_not_found())
Ok(acp::LoadSessionResponse { modes: None })
}

async fn prompt(
Expand Down
Loading