Skip to content

Commit 4e66202

Browse files
committed
fix(acp): merge consecutive error messages
1 parent 620fb30 commit 4e66202

2 files changed

Lines changed: 89 additions & 35 deletions

File tree

anycode-backend/src/acp.rs

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::acp_fs::AcpFsCommand;
22
use crate::acp_history::AcpHistoryManager;
3-
use agent_client_protocol::{ByteStreams, Client, ConnectionTo};
43
use agent_client_protocol::schema as acp;
4+
use agent_client_protocol::{ByteStreams, Client, ConnectionTo};
55
use agent_client_protocol_schema::{ProtocolVersion, SessionId};
66
use anyhow::{Context, Result, anyhow};
77
use serde::{Deserialize, Serialize};
@@ -1042,6 +1042,27 @@ impl AcpClientImpl {
10421042
}
10431043
}
10441044

1045+
fn append_or_push_error_message(history: &mut Vec<AcpMessage>, text: &str) -> AcpMessage {
1046+
if let Some(last_idx) = history.len().checked_sub(1) {
1047+
if let AcpMessage::Error(AcpError { message }) = &history[last_idx] {
1048+
let mut merged = message.clone();
1049+
if !merged.is_empty() {
1050+
merged.push('\n');
1051+
}
1052+
merged.push_str(text);
1053+
let merged_error = AcpMessage::Error(AcpError { message: merged });
1054+
history[last_idx] = merged_error.clone();
1055+
return merged_error;
1056+
}
1057+
}
1058+
1059+
let error = AcpMessage::Error(AcpError {
1060+
message: text.to_string(),
1061+
});
1062+
history.push(error.clone());
1063+
error
1064+
}
1065+
10451066
async fn send_message(&self, message: AcpMessage) {
10461067
match self.message_sender.send(message) {
10471068
Ok(receiver_count) => {
@@ -1387,37 +1408,58 @@ impl AcpAgent {
13871408
)
13881409
.on_receive_request(
13891410
async move |req: acp::ReleaseTerminalRequest, responder, _cx| {
1390-
responder.respond(client_impl_for_release_terminal.release_terminal(req).await?)
1411+
responder.respond(
1412+
client_impl_for_release_terminal
1413+
.release_terminal(req)
1414+
.await?,
1415+
)
13911416
},
13921417
agent_client_protocol::on_receive_request!(),
13931418
)
13941419
.on_receive_request(
13951420
async move |req: acp::WaitForTerminalExitRequest, responder, _cx| {
1396-
responder.respond(client_impl_for_wait_terminal.wait_for_terminal_exit(req).await?)
1421+
responder.respond(
1422+
client_impl_for_wait_terminal
1423+
.wait_for_terminal_exit(req)
1424+
.await?,
1425+
)
13971426
},
13981427
agent_client_protocol::on_receive_request!(),
13991428
)
14001429
.on_receive_request(
14011430
async move |req: acp::KillTerminalRequest, responder, _cx| {
1402-
responder.respond(client_impl_for_kill_terminal.kill_terminal_command(req).await?)
1431+
responder.respond(
1432+
client_impl_for_kill_terminal
1433+
.kill_terminal_command(req)
1434+
.await?,
1435+
)
14031436
},
14041437
agent_client_protocol::on_receive_request!(),
14051438
)
14061439
.on_receive_notification(
14071440
async move |notif: acp::SessionNotification, _cx| {
1408-
client_impl_for_session_notification.session_notification(notif).await
1441+
client_impl_for_session_notification
1442+
.session_notification(notif)
1443+
.await
14091444
},
14101445
agent_client_protocol::on_receive_notification!(),
14111446
)
14121447
.connect_with(transport, async move |conn| {
1413-
let bootstrap = match Self::initialize_connection(&conn, &agent_id_for_conn, resume_session_id).await {
1414-
Ok(value) => value,
1415-
Err(e) => {
1416-
let err = anyhow!("Failed to initialize ACP agent {}: {}", agent_id_for_conn, e);
1417-
let _ = bootstrap_tx.send(Err(err));
1418-
return Err(acp::Error::internal_error().data(format!("{e:#}")));
1419-
}
1420-
};
1448+
let bootstrap =
1449+
match Self::initialize_connection(&conn, &agent_id_for_conn, resume_session_id)
1450+
.await
1451+
{
1452+
Ok(value) => value,
1453+
Err(e) => {
1454+
let err = anyhow!(
1455+
"Failed to initialize ACP agent {}: {}",
1456+
agent_id_for_conn,
1457+
e
1458+
);
1459+
let _ = bootstrap_tx.send(Err(err));
1460+
return Err(acp::Error::internal_error().data(format!("{e:#}")));
1461+
}
1462+
};
14211463

14221464
ready.store(true, Ordering::SeqCst);
14231465
let _ = bootstrap_tx.send(Ok(bootstrap.clone()));
@@ -1466,17 +1508,14 @@ impl AcpAgent {
14661508
let error_msg = buf.trim().to_string();
14671509
error!("ACP stderr: {}", error_msg);
14681510

1469-
// Create error message
1470-
let error_message = AcpMessage::Error(AcpError { message: error_msg });
1471-
14721511
// Save to history
1473-
{
1512+
let message_for_ui = {
14741513
let mut history = history.lock().await;
1475-
history.push(error_message.clone());
1476-
}
1514+
AcpClientImpl::append_or_push_error_message(&mut history, &error_msg)
1515+
};
14771516

14781517
// Send error message to UI
1479-
let _ = message_sender.send(error_message);
1518+
let _ = message_sender.send(message_for_ui);
14801519

14811520
buf.clear();
14821521
}
@@ -1895,19 +1934,15 @@ impl AcpAgent {
18951934
}
18961935
Err(e) => {
18971936
error!("Failed to end prompt for agent {}, error: {}", agent_id, e);
1898-
// Create error message
1899-
let error_message = AcpMessage::Error(AcpError {
1900-
message: e.to_string(),
1901-
});
19021937

19031938
// Save to history
1904-
{
1939+
let message_for_ui = {
19051940
let mut hist = history.lock().await;
1906-
hist.push(error_message.clone());
1907-
}
1941+
AcpClientImpl::append_or_push_error_message(&mut hist, &e.to_string())
1942+
};
19081943

19091944
// Send error message to UI
1910-
let _ = message_sender.send(error_message);
1945+
let _ = message_sender.send(message_for_ui);
19111946
}
19121947
}
19131948
break;
@@ -1971,7 +2006,9 @@ impl AcpAgent {
19712006
.connect_with(transport, async move |conn| {
19722007
Self::initialize_agent_connection(&conn, "session-list")
19732008
.await
1974-
.map_err(|e| acp::Error::internal_error().data(format!("{e:#}")))?;
2009+
.map_err(|e| {
2010+
acp::Error::internal_error().data(format!("{e:#}"))
2011+
})?;
19752012

19762013
let mut all_sessions = Vec::new();
19772014
let mut cursor = None;
@@ -1986,14 +2023,14 @@ impl AcpAgent {
19862023
.block_task()
19872024
.await?;
19882025

1989-
all_sessions.extend(response.sessions.into_iter().map(|session| {
1990-
AcpSessionSummary {
2026+
all_sessions.extend(response.sessions.into_iter().map(
2027+
|session| AcpSessionSummary {
19912028
session_id: session.session_id.to_string(),
19922029
cwd: session.cwd.to_string_lossy().to_string(),
19932030
title: session.title,
19942031
updated_at: session.updated_at,
1995-
}
1996-
}));
2032+
},
2033+
));
19972034

19982035
if response.next_cursor.is_none() {
19992036
break;

anycode/hooks/useAgents.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ export const useAgents = ({
3232
openFileDiff,
3333
onAgentStarted,
3434
}: UseAgentsParams) => {
35+
const mergeConsecutiveErrors = (messages: AcpMessage[]): AcpMessage[] => {
36+
const merged: AcpMessage[] = [];
37+
for (const message of messages) {
38+
const last = merged[merged.length - 1];
39+
if (message.role === 'error' && last?.role === 'error') {
40+
merged[merged.length - 1] = {
41+
...last,
42+
message: `${last.message}\n${message.message}`,
43+
};
44+
continue;
45+
}
46+
merged.push(message);
47+
}
48+
return merged;
49+
};
50+
3551
const [acpSessions, setAcpSessions] = useState<Map<string, AcpSession>>(new Map());
3652
const [selectedAgentId, setSelectedAgentId] = useState<string | null>(null);
3753
const [isAgentSettingsOpen, setIsAgentSettingsOpen] = useState<boolean>(false);
@@ -197,7 +213,7 @@ export const useAgents = ({
197213
updateSession(data.agent_id, (existing) => ({
198214
agentId: data.agent_id,
199215
agentName: existing?.agentName ?? '',
200-
messages: [...(existing?.messages ?? []), data.item],
216+
messages: mergeConsecutiveErrors([...(existing?.messages ?? []), data.item]),
201217
isActive: true,
202218
isProcessing: existing?.isProcessing,
203219
sessionId: existing?.sessionId,
@@ -295,11 +311,12 @@ export const useAgents = ({
295311
&& item.role !== 'session_reasoning_selector'
296312
&& item.role !== 'context_usage',
297313
);
314+
const mergedVisibleMessages = mergeConsecutiveErrors(visibleMessages);
298315

299316
updateSession(data.agent_id, (existing) => ({
300317
agentId: data.agent_id,
301318
agentName: existing?.agentName ?? '',
302-
messages: visibleMessages,
319+
messages: mergedVisibleMessages,
303320
isActive: true,
304321
isProcessing: existing?.isProcessing,
305322
sessionId: existing?.sessionId,

0 commit comments

Comments
 (0)