Skip to content
Open
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
7 changes: 6 additions & 1 deletion examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,12 @@ impl acp::Client for ExampleClient {
};
println!("| Agent: {text}");
}
_ => {} // Handle future variants gracefully
acp::SessionUpdate::AgentMessageClear => {
// Clear accumulated streamed content for the current agent message.
// Subsequent agent_message_chunk will append from empty.
println!("| Agent: (message cleared)");
}
_ => {} // Handle other variants gracefully
}
Ok(())
}
Expand Down
6 changes: 6 additions & 0 deletions src/agent-client-protocol/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [Unreleased]

### Added

- Support for `agent_message_clear` session update (RFD [agent_message_clear](https://github.com/agentclientprotocol/agent-client-protocol/blob/main/docs/rfds/agent_message_clear.md)): clients can clear accumulated streamed content for the current agent message; example client and tests updated.

## [0.9.4](https://github.com/agentclientprotocol/rust-sdk/compare/v0.9.3...v0.9.4) - 2026-02-04

### Added
Expand Down
68 changes: 68 additions & 0 deletions src/agent-client-protocol/src/rpc_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,9 @@ async fn test_full_conversation_flow() {
found_final_message = true;
}
}
SessionUpdate::AgentMessageClear => {
// Client would clear accumulated message; test just verifies we receive it.
}
SessionUpdate::ToolCall(_) => {
found_tool_call = true;
}
Expand All @@ -647,6 +650,71 @@ async fn test_full_conversation_flow() {
.await;
}

#[tokio::test]
async fn test_agent_message_clear_sequence() {
let local_set = tokio::task::LocalSet::new();
local_set
.run_until(async {
let client = TestClient::new();
let agent = TestAgent::new();
let (agent_conn, client_conn) = create_connection_pair(&client, &agent);

let session_id = SessionId::new("clear-test-session");
agent_conn
.prompt(PromptRequest::new(
session_id.clone(),
vec!["Say hello then replace with goodbye".into()],
))
.await
.expect("prompt failed");

// Agent sends draft, then clear, then final (RFD agent_message_clear usage pattern)
client_conn
.session_notification(SessionNotification::new(
session_id.clone(),
SessionUpdate::AgentMessageChunk(ContentChunk::new("Drafting...".into())),
))
.await
.expect("session_notification failed");
client_conn
.session_notification(SessionNotification::new(
session_id.clone(),
SessionUpdate::AgentMessageClear,
))
.await
.expect("session_notification failed");
client_conn
.session_notification(SessionNotification::new(
session_id.clone(),
SessionUpdate::AgentMessageChunk(ContentChunk::new("Here is the final result.".into())),
))
.await
.expect("session_notification failed");

for _ in 0..10 {
tokio::task::yield_now().await;
}

let updates = client.session_notifications.lock().unwrap();
let mut clear_seen = false;
let mut final_chunk_seen = false;
for n in updates.iter() {
match &n.update {
SessionUpdate::AgentMessageClear => clear_seen = true,
SessionUpdate::AgentMessageChunk(ContentChunk { content: ContentBlock::Text(t), .. }) => {
if t.text.contains("final result") {
final_chunk_seen = true;
}
}
_ => {}
}
}
assert!(clear_seen, "Client should receive agent_message_clear");
assert!(final_chunk_seen, "Client should receive final chunk after clear");
})
.await;
}

#[tokio::test]
async fn test_extension_methods_and_notifications() {
let local_set = tokio::task::LocalSet::new();
Expand Down
16 changes: 11 additions & 5 deletions src/sacp-test/src/test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,17 @@ where
JrHandlerChain::new()
.name("test-client")
.on_receive_notification(async |notif: SessionNotification, _cx| {
// Collect text from AgentMessageChunk updates
if let SessionUpdate::AgentMessageChunk(chunk) = &notif.update
&& let ContentBlock::Text(text_content) = &chunk.content
{
collected_text.push_str(&text_content.text);
// Collect text from AgentMessageChunk updates; clear on AgentMessageClear
match &notif.update {
SessionUpdate::AgentMessageChunk(chunk) => {
if let ContentBlock::Text(text_content) = &chunk.content {
collected_text.push_str(&text_content.text);
}
}
SessionUpdate::AgentMessageClear => {
collected_text.clear();
}
_ => {}
}
Ok(())
})
Expand Down