Skip to content

Commit 110d86c

Browse files
committed
Rewind
1 parent ebc5801 commit 110d86c

5 files changed

Lines changed: 81 additions & 1 deletion

File tree

docs/protocol/initialization.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ The Agent **SHOULD** specify whether it supports the following capabilities:
118118
The `session/load` method is available.
119119
</ResponseField>
120120

121+
<ResponseField name="rewindSession" type="boolean" post={["default: false"]}>
122+
The `session/rewind` method is available.
123+
</ResponseField>
124+
121125
<ResponseField name="promptCapabilities" type="PromptCapabilities Object">
122126
Object indicating the different types of content that may be included in
123127
`session/prompt` requests.

docs/protocol/overview.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ Agents are programs that use generative AI to autonomously modify code. They typ
6262
Load an existing session (requires `loadSession` capability).
6363
</ParamField>
6464

65+
<ParamField path="session/rewind">
66+
Rewinds an active session to an earlier message (requires `rewindSession` capability).
67+
</ParamField>
68+
6569
### Notifications
6670

6771
<ParamField path="session/cancel">

rust/acp.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,22 @@ impl fmt::Display for SessionId {
101101
}
102102
}
103103

104+
/// A unique identifier for a prompt sent from the client to the agent.
105+
///
106+
/// This is used when both the agent and the client support rewinding conversations
107+
/// to identify the point in the conversation to rewind to.
108+
///
109+
/// See: <https://agentclientprotocol.com/protocol/session-setup#session-id>
110+
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
111+
#[serde(transparent)]
112+
pub struct PromptId(pub Arc<str>);
113+
114+
impl fmt::Display for PromptId {
115+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116+
write!(f, "{}", self.0)
117+
}
118+
}
119+
104120
// Client to Agent
105121

106122
/// A client-side connection to an agent.
@@ -195,6 +211,15 @@ impl Agent for ClientSideConnection {
195211
.await
196212
}
197213

214+
async fn rewind(&self, arguments: RewindRequest) -> Result<(), Error> {
215+
self.conn
216+
.request(
217+
SESSION_REWIND_METHOD_NAME,
218+
Some(ClientRequest::RewindRequest(arguments)),
219+
)
220+
.await
221+
}
222+
198223
async fn prompt(&self, arguments: PromptRequest) -> Result<PromptResponse, Error> {
199224
self.conn
200225
.request(
@@ -460,6 +485,10 @@ impl<T: Agent> MessageHandler<AgentSide> for T {
460485
let response = self.prompt(args).await?;
461486
Ok(AgentResponse::PromptResponse(response))
462487
}
488+
ClientRequest::RewindRequest(args) => {
489+
self.rewind(args).await?;
490+
Ok(AgentResponse::RewindResponse)
491+
}
463492
}
464493
}
465494

rust/agent.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use anyhow::Result;
99
use schemars::JsonSchema;
1010
use serde::{Deserialize, Serialize};
1111

12-
use crate::{ClientCapabilities, ContentBlock, Error, ProtocolVersion, SessionId};
12+
use crate::{ClientCapabilities, ContentBlock, Error, PromptId, ProtocolVersion, SessionId};
1313

1414
/// The Agent trait defines the interface that all ACP-compliant agents must implement.
1515
///
@@ -91,6 +91,11 @@ pub trait Agent {
9191
arguments: PromptRequest,
9292
) -> impl Future<Output = Result<PromptResponse, Error>>;
9393

94+
/// Rewinds a session.
95+
///
96+
/// See: <https://agentclientprotocol.com/protocol/prompt-turn>
97+
fn rewind(&self, arguments: RewindRequest) -> impl Future<Output = Result<(), Error>>;
98+
9499
/// Cancels ongoing operations for a session.
95100
///
96101
/// This is a notification sent by the client to cancel an ongoing prompt turn.
@@ -218,6 +223,26 @@ pub struct LoadSessionRequest {
218223
pub session_id: SessionId,
219224
}
220225

226+
// Rewind session
227+
228+
/// Request parameters for rewinding a session.
229+
///
230+
/// Only available if the agent supports the `rewindSession` capability.
231+
///
232+
/// The conversation is rewound to the moment just before the given prompt was
233+
/// submitted. This allows the user to submit a new version of that prompt through
234+
/// the UI to attempt to get better results.
235+
///
236+
/// See: <https://agentclientprotocol.com/protocol/session-setup#truncating-sessions>
237+
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
238+
#[serde(rename_all = "camelCase")]
239+
pub struct RewindRequest {
240+
/// The prompt ID to rewind to. This prompt, and all subsequent user and agent
241+
/// messages should be discarded.
242+
pub prompt_id: PromptId,
243+
pub session_id: SessionId,
244+
}
245+
221246
// MCP
222247

223248
/// Configuration for connecting to an MCP (Model Context Protocol) server.
@@ -261,6 +286,9 @@ pub struct EnvVariable {
261286
pub struct PromptRequest {
262287
/// The ID of the session to send this user message to
263288
pub session_id: SessionId,
289+
/// The ID of the prompt. Required if both the client and
290+
/// the server support rewinding.
291+
pub prompt_id: Option<PromptId>,
264292
/// The blocks of content that compose the user's message.
265293
///
266294
/// As a baseline, the Agent MUST support [`ContentBlock::Text`] and [`ContentBlock::ResourceLink`],
@@ -329,6 +357,9 @@ pub struct AgentCapabilities {
329357
/// Whether the agent supports `session/load`.
330358
#[serde(default)]
331359
pub load_session: bool,
360+
/// Whether the agent supports `session/rewind`.
361+
#[serde(default)]
362+
pub rewind_session: bool,
332363
/// Prompt capabilities supported by the agent.
333364
#[serde(default)]
334365
pub prompt_capabilities: PromptCapabilities,
@@ -378,6 +409,8 @@ pub struct AgentMethodNames {
378409
pub session_new: &'static str,
379410
/// Method for loading an existing session.
380411
pub session_load: &'static str,
412+
/// Method for rewinding the current session.
413+
pub session_rewind: &'static str,
381414
/// Method for sending a prompt to the agent.
382415
pub session_prompt: &'static str,
383416
/// Notification for cancelling operations.
@@ -390,6 +423,7 @@ pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
390423
authenticate: AUTHENTICATE_METHOD_NAME,
391424
session_new: SESSION_NEW_METHOD_NAME,
392425
session_load: SESSION_LOAD_METHOD_NAME,
426+
session_rewind: SESSION_REWIND_METHOD_NAME,
393427
session_prompt: SESSION_PROMPT_METHOD_NAME,
394428
session_cancel: SESSION_CANCEL_METHOD_NAME,
395429
};
@@ -402,6 +436,8 @@ pub(crate) const AUTHENTICATE_METHOD_NAME: &str = "authenticate";
402436
pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
403437
/// Method name for loading an existing session.
404438
pub(crate) const SESSION_LOAD_METHOD_NAME: &str = "session/load";
439+
/// Method name for truncating the current session.
440+
pub(crate) const SESSION_REWIND_METHOD_NAME: &str = "session/rewind";
405441
/// Method name for sending a prompt.
406442
pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
407443
/// Method name for the cancel notification.
@@ -420,6 +456,7 @@ pub enum ClientRequest {
420456
AuthenticateRequest(AuthenticateRequest),
421457
NewSessionRequest(NewSessionRequest),
422458
LoadSessionRequest(LoadSessionRequest),
459+
RewindRequest(RewindRequest),
423460
PromptRequest(PromptRequest),
424461
}
425462

@@ -436,6 +473,7 @@ pub enum AgentResponse {
436473
AuthenticateResponse,
437474
NewSessionResponse(NewSessionResponse),
438475
LoadSessionResponse,
476+
RewindResponse,
439477
PromptResponse(PromptResponse),
440478
}
441479

rust/rpc_tests.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ impl Agent for TestAgent {
114114
Ok(())
115115
}
116116

117+
async fn rewind(&self, _arguments: RewindRequest) -> Result<(), Error> {
118+
unimplemented!()
119+
}
120+
117121
async fn prompt(&self, arguments: PromptRequest) -> Result<PromptResponse, Error> {
118122
self.prompts_received
119123
.lock()
@@ -405,6 +409,7 @@ async fn test_full_conversation_flow() {
405409

406410
agent_conn
407411
.prompt(PromptRequest {
412+
prompt_id: None,
408413
session_id: session_id.clone(),
409414
prompt: user_prompt,
410415
})

0 commit comments

Comments
 (0)