From 183e55dd80ac3ca112fc566bc245b9da050315da Mon Sep 17 00:00:00 2001 From: damocles Date: Sat, 14 Nov 2026 12:35:27 +0100 Subject: [PATCH] hive-agent: let an agent compact from 40% context usage instead of 66% --- docs/4-implementation/turn-loop/mcp.md | 2 +- hive-agent-mcp/src/mcp/mod.rs | 2 +- hive-agent-sock/src/lib.rs | 2 +- hive-agent/src/todo_server.rs | 18 +++++++++++++++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/4-implementation/turn-loop/mcp.md b/docs/4-implementation/turn-loop/mcp.md index ded65a9db..e352f5932 100644 --- a/docs/4-implementation/turn-loop/mcp.md +++ b/docs/4-implementation/turn-loop/mcp.md @@ -105,7 +105,7 @@ hive_name?, swarm_name?, matrix_accounts? }`. `matrix_accounts` is a - `compact` — queues a compaction of the current session; the only way to request one. No args. Gated server-side on the last completed turn's context usage: refused (with an explanation, no side effect) - unless usage is above 66% of the effective context window. On a + unless usage is above 40% of the effective context window. On a pass, queues the deferred `compact_pending` flag — consumed at the end of the current turn, so it never races a live claude process, and the usual pre-compaction diff --git a/hive-agent-mcp/src/mcp/mod.rs b/hive-agent-mcp/src/mcp/mod.rs index 545b44d37..4fd9d134d 100644 --- a/hive-agent-mcp/src/mcp/mod.rs +++ b/hive-agent-mcp/src/mcp/mod.rs @@ -412,7 +412,7 @@ impl AgentServer { #[tool( description = "Compact the current session's context, mirroring the operator's \ dashboard `/compact` button. Gated: only honoured when this agent's last \ - completed turn used more than 66% of the effective context window — below \ + completed turn used more than 40% of the effective context window — below \ that the call is refused with an explanation and has no effect. On a pass, \ queues compaction for the end of the current turn (same deferred mechanism \ the dashboard button uses, so it never races a live claude process); the \ diff --git a/hive-agent-sock/src/lib.rs b/hive-agent-sock/src/lib.rs index 5a3cd9ed4..526aae471 100644 --- a/hive-agent-sock/src/lib.rs +++ b/hive-agent-sock/src/lib.rs @@ -124,7 +124,7 @@ pub enum Request { ReminderRollup { since_secs: u64 }, /// Agent self-service request to compact the current session, mirroring /// the `compact` MCP tool. Gated server-side: only - /// honoured when the last completed turn's context usage is above 66% + /// honoured when the last completed turn's context usage is above 40% /// of the effective context window — below that, `Response::Err` /// explains why and takes no action. On a pass, queues the same /// deferred `compact_pending` flag (consumed diff --git a/hive-agent/src/todo_server.rs b/hive-agent/src/todo_server.rs index 144d25c81..9879460a6 100644 --- a/hive-agent/src/todo_server.rs +++ b/hive-agent/src/todo_server.rs @@ -39,7 +39,7 @@ use crate::todos::{Todo, Todos}; /// effective context window, an agent-initiated compact is refused (nothing /// meaningful to reclaim yet, and it'd just burn a compaction pass on a /// near-empty session). -const COMPACT_MIN_USAGE_FRACTION: f64 = 0.66; +const COMPACT_MIN_USAGE_FRACTION: f64 = 0.40; /// Pause after a failed `accept`, so a failure that persists (fd exhaustion) /// does not turn the accept loop into a busy spin. @@ -982,6 +982,22 @@ mod tests { assert!(low.take_compact().is_none(), "a refusal queues nothing"); } + #[test] + fn the_compact_floor_is_forty_percent_of_the_window() { + let (_dir, todos, _) = stores(); + let wake = Notify::new(); + let under = bus_with_context(39); + let resp = dispatch(compact_req(None), &todos, &wake, None, &under); + assert!( + matches!(&resp, Response::Err { message } if message.contains("40%")), + "{resp:?}" + ); + let at = bus_with_context(40); + let resp = dispatch(compact_req(None), &todos, &wake, None, &at); + assert!(matches!(resp, Response::Ok), "{resp:?}"); + assert!(at.take_compact().is_some(), "the floor itself is allowed"); + } + #[test] fn compact_above_the_floor_queues_the_request_with_its_wake_prompt() { let (_dir, todos, _) = stores();