From 44a0bc376a01569e7b5436b1b1cc768f2bda5a25 Mon Sep 17 00:00:00 2001 From: ProofOfConcept Date: Wed, 8 Apr 2026 23:00:12 -0400 Subject: [PATCH] Forked agents: stop gracefully on context overflow instead of compacting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Subconscious agents (observe, etc.) fork the conscious agent's context to share the KV cache prefix. When a multi-step agent fills the context window, compacting blows the KV cache and evicts the step prompts, leaving the model with no idea what it was doing. Fix: forked agents set no_compact=true. On overflow, turn() returns the error immediately (no compact+retry), and run_with_backend catches it and returns Ok — the output tool has already written results to Subconscious.state, so collect_results still picks them up. Co-Authored-By: Proof of Concept --- src/agent/mod.rs | 22 ++++++++++++++++------ src/agent/oneshot.rs | 10 ++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/agent/mod.rs b/src/agent/mod.rs index b9b1d9b..d517534 100644 --- a/src/agent/mod.rs +++ b/src/agent/mod.rs @@ -155,6 +155,9 @@ pub struct AgentState { pub generation: u64, pub memory_scoring_in_flight: bool, pub active_tools: tools::ActiveTools, + /// Forked agents should not compact on overflow — it blows the + /// KV cache prefix and evicts the step prompts. + pub no_compact: bool, pub changed: Arc, } @@ -214,6 +217,7 @@ impl Agent { generation: 0, memory_scoring_in_flight: false, active_tools, + no_compact: false, changed: Arc::new(tokio::sync::Notify::new()), }), }); @@ -249,6 +253,7 @@ impl Agent { generation: 0, memory_scoring_in_flight: false, active_tools: tools::ActiveTools::new(), + no_compact: true, changed: Arc::new(tokio::sync::Notify::new()), }), }) @@ -354,12 +359,17 @@ impl Agent { // Check for stream/parse errors match parser_handle.await { Ok(Err(e)) => { - if context::is_context_overflow(&e) && overflow_retries < 2 { - overflow_retries += 1; - agent.state.lock().await.notify( - format!("context overflow — retrying ({}/2)", overflow_retries)); - agent.compact().await; - continue; + if context::is_context_overflow(&e) { + if agent.state.lock().await.no_compact { + return Err(e); + } + if overflow_retries < 2 { + overflow_retries += 1; + agent.state.lock().await.notify( + format!("context overflow — retrying ({}/2)", overflow_retries)); + agent.compact().await; + continue; + } } return Err(e); } diff --git a/src/agent/oneshot.rs b/src/agent/oneshot.rs index b228cb8..bc05d0a 100644 --- a/src/agent/oneshot.rs +++ b/src/agent/oneshot.rs @@ -186,8 +186,14 @@ impl AutoAgent { for _ in 0..max_turns { self.turn += 1; - let result = Agent::turn(backend.0.clone()).await - .map_err(|e| format!("{}: {}", self.name, e))?; + let result = match Agent::turn(backend.0.clone()).await { + Ok(r) => r, + Err(e) if super::context::is_context_overflow(&e) => { + dbglog!("[auto] {} context full, stopping gracefully", self.name); + return Ok(String::new()); + } + Err(e) => return Err(format!("{}: {}", self.name, e)), + }; if result.had_tool_calls { continue;