From e5ea2737a73cf368816ca10e20c624e326e22852 Mon Sep 17 00:00:00 2001 From: Tyler Longwell Date: Mon, 11 May 2026 15:41:55 -0400 Subject: [PATCH] fix: classify agent JSON-RPC errors as application errors, not transport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When sprout-agent returns a JSON-RPC error response (e.g. transient LLM failure), the harness was classifying it as AcpError::Protocol — a transport-class error that triggers agent respawn. But the stdio pipe is intact and the agent is healthy; only the upstream LLM call failed. Add AcpError::AgentError for well-formed JSON-RPC error responses from the agent. This variant is not in the transport-error match in handle_prompt_result(), so the agent is returned to the pool instead of being killed and respawned. Before: transient LLM timeout → agent respawn → session loss for all channels After: transient LLM timeout → agent returned to pool → next prompt works --- crates/sprout-acp/src/acp.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/sprout-acp/src/acp.rs b/crates/sprout-acp/src/acp.rs index bc81a9e29..5b4dcd49b 100644 --- a/crates/sprout-acp/src/acp.rs +++ b/crates/sprout-acp/src/acp.rs @@ -101,6 +101,9 @@ pub enum AcpError { #[error("Protocol error: {0}")] Protocol(String), + + #[error("Agent reported error: {0}")] + AgentError(String), } // ─── AcpClient ─────────────────────────────────────────────────────────────── @@ -709,7 +712,7 @@ impl AcpClient { if let Some(id) = msg.get("id") { if *id == serde_json::json!(expected_id) && msg.get("method").is_none() { if let Some(error) = msg.get("error") { - return Err(AcpError::Protocol(error.to_string())); + return Err(AcpError::AgentError(error.to_string())); } return Ok(msg["result"].clone()); } @@ -823,7 +826,7 @@ impl AcpClient { if let Some(id) = msg.get("id") { if *id == serde_json::json!(expected_id) && msg.get("method").is_none() { if let Some(error) = msg.get("error") { - return Err(AcpError::Protocol(error.to_string())); + return Err(AcpError::AgentError(error.to_string())); } return Ok(msg["result"].clone()); }