diff --git a/crates/ide-agent/src/lib.rs b/crates/ide-agent/src/lib.rs index 4a777949e..c35239d66 100644 --- a/crates/ide-agent/src/lib.rs +++ b/crates/ide-agent/src/lib.rs @@ -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> { .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") }) } diff --git a/crates/ide-agent/src/tests.rs b/crates/ide-agent/src/tests.rs index 13823a918..3dc27a4d0 100644 --- a/crates/ide-agent/src/tests.rs +++ b/crates/ide-agent/src/tests.rs @@ -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"); +}