fix(agent): show the details an agent sends with an error (#2563)

ACP agents report failures as a generic JSON-RPC error and put the cause in data.details (or a string data). Only the message was shown, so such failures read just "Internal error". The details are now appended unless the message already contains them.
This commit is contained in:
l0ng-ai
2026-11-10 23:57:29 +08:00
committed by GitHub
parent 51e4ec0ab9
commit 283b3bbeea
2 changed files with 33 additions and 1 deletions
+19 -1
View File
@@ -1722,7 +1722,7 @@ impl Task {
format!("The agent process exited:\n{}", tail.join("\n"))
}
}
RpcError::Server { message, .. } => message,
RpcError::Server { message, data, .. } => with_details(message, data.as_ref()),
e => e.to_string(),
}
}
@@ -2180,6 +2180,24 @@ fn usage_limit(e: &RpcError) -> Option<Option<f64>> {
.then_some(None)
}
/// An error's message with the detail ACP agents put in `data` (`{"details": …}` or a string):
/// generic codes such as "Internal error" say nothing without it.
fn with_details(message: String, data: Option<&Value>) -> String {
let details = match data {
Some(Value::String(text)) => text.as_str(),
Some(data) => data.get("details").and_then(Value::as_str).unwrap_or_default(),
None => "",
}
.trim();
if details.is_empty() || message.contains(details) {
message
} else if message.trim().is_empty() {
details.to_owned()
} else {
format!("{message}: {details}")
}
}
fn client_info() -> Value {
json!({ "name": "OpenIDEA", "version": env!("CARGO_PKG_VERSION") })
}
+14
View File
@@ -2568,3 +2568,17 @@ fn streamed_text_is_sent_as_appends() {
let rewritten = entry_event("t", &mut streamed, None, 5, &agent(&format!("{}x!??", "é".repeat(4999))));
assert!(matches!(rewritten, AgentEvent::Entry { .. }), "a rewritten end is sent whole");
}
#[test]
fn error_messages_keep_the_agents_details() {
let details = json!({ "details": "spawn claude ENOENT" });
assert_eq!(with_details("Internal error".into(), Some(&details)), "Internal error: spawn claude ENOENT");
assert_eq!(
with_details("Internal error".into(), Some(&json!("model not found"))),
"Internal error: model not found"
);
assert_eq!(with_details(String::new(), Some(&details)), "spawn claude ENOENT");
assert_eq!(with_details("spawn claude ENOENT".into(), Some(&details)), "spawn claude ENOENT");
assert_eq!(with_details("Internal error".into(), Some(&json!({ "other": 1 }))), "Internal error");
assert_eq!(with_details("Internal error".into(), None), "Internal error");
}