From c99261b0426f62eeeae111d7ae049556f6866d75 Mon Sep 17 00:00:00 2001 From: damocles Date: Fri, 22 May 2026 00:35:13 +0200 Subject: [PATCH] =?UTF-8?q?harness:=20add=20request=5Fnext=5Fturn=20MCP=20?= =?UTF-8?q?tool=20=E2=80=94=20immediate=20self-continuation=20(closes=20#2?= =?UTF-8?q?16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hive-ag3nt/prompts/agent.md | 1 + hive-ag3nt/src/bin/hive-ag3nt.rs | 43 ++++++++++++++++++++++++++++++++ hive-ag3nt/src/mcp.rs | 24 ++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/hive-ag3nt/prompts/agent.md b/hive-ag3nt/prompts/agent.md index 89a3735..8d204f4 100644 --- a/hive-ag3nt/prompts/agent.md +++ b/hive-ag3nt/prompts/agent.md @@ -11,6 +11,7 @@ Tools (hyperhive surface): - `mcp__hyperhive__cancel_loose_end(kind, id)` — cancel one of your own open threads. `kind` is `"question"` (the asker — you, in this case — gets a `[cancelled by ]` answer so the waiter unblocks) or `"reminder"` (hard-deleted before it fires). `id` from the matching `get_loose_ends` row or the original submission reply. - `mcp__hyperhive__remind(message, delay_seconds? | at_unix_timestamp?, file_path?)` — schedule a message to land in your *own* inbox at a future time (sender shows as `reminder`). Set exactly one of `delay_seconds` (relative) or `at_unix_timestamp` (absolute). Use for self-paced follow-ups instead of blocking a whole turn on a long `recv` wait. A large `message` auto-spills to a file under `/agents/{label}/state/reminders/`; pass `file_path` to point at one yourself. Each agent's pending-reminder count is capped (default 50) — the tool will error if the cap is already reached. - `mcp__hyperhive__whoami()` — self-introspection: returns your canonical agent name (from socket identity, not the prompt-substituted label), role, and current hyperhive rev. No args. Use it when you want a trustworthy identity stamp for state files, commit messages, or cross-agent attribution that won't drift across renames. +- `mcp__hyperhive__request_next_turn()` — ask the harness to start another turn immediately after this one ends, even if the inbox is empty. Use for multi-turn tasks (long builds, sequential steps) where you want to continue without waiting for an external message. The next turn starts with `from: "self"` and `body: "continue"`. No-op if new inbox messages arrive before this turn ends (the harness already loops immediately on pending messages). No args. Need new packages, env vars, or other NixOS config for yourself? You can't edit your own config directly — message the manager (recipient `manager`) describing what you need + why. The manager evaluates the request (it doesn't rubber-stamp), edits `/agents/{label}/config/agent.nix` on your behalf, commits, and submits an approval that the operator can accept on the dashboard; on approve hive-c0re rebuilds your container with the new config. diff --git a/hive-ag3nt/src/bin/hive-ag3nt.rs b/hive-ag3nt/src/bin/hive-ag3nt.rs index 9a393e2..4b06199 100644 --- a/hive-ag3nt/src/bin/hive-ag3nt.rs +++ b/hive-ag3nt/src/bin/hive-ag3nt.rs @@ -273,6 +273,11 @@ async fn serve( if pending > 0 { tracing::info!(%pending, "pending messages after turn; fetching next"); } + // `request_next_turn` MCP tool: agent wrote a sentinel + // requesting an immediate self-continuation turn. Clear + // the file and inject a synthetic wake so the outer loop + // fires a bare turn even if the inbox is empty. + check_and_inject_continue(socket, label).await; } Ok(AgentResponse::Messages { .. }) => { // Idle: empty list = nothing pending. Brief sleep @@ -408,3 +413,41 @@ async fn fetch_agent_post_turn_counts(socket: &Path) -> (Option, Option( + socket, + &AgentRequest::Wake { + from: "self".into(), + body: "continue".into(), + }, + ) + .await; + match res { + Ok(AgentResponse::Ok) => { + tracing::info!(%label, "request_next_turn: injected self-continue wake"); + } + Ok(AgentResponse::Err { message }) => { + tracing::warn!(%message, "check_and_inject_continue: wake rejected"); + } + Err(e) => { + tracing::warn!(error = ?e, "check_and_inject_continue: wake transport error"); + } + _ => {} + } +} + diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index 0c9e893..fd3ad1a 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -626,6 +626,30 @@ impl AgentServer { }) .await } + + #[tool( + description = "Ask the harness to start another turn immediately after this one \ + completes, even if the inbox is empty. Use this when you have ongoing work that \ + spans multiple turns (long builds, multi-step tasks) and you want to continue \ + without waiting for an external message. The next turn will start with \ + `from: \"self\"` and `body: \"continue\"`. Has no effect if a new inbox message \ + arrives before this turn ends — the harness already loops immediately on pending \ + messages. No args." + )] + async fn request_next_turn(&self) -> String { + run_tool_envelope("request_next_turn", String::new(), async move { + let sentinel = crate::paths::state_dir().join("hyperhive-continue"); + match std::fs::write(&sentinel, b"") { + Ok(()) => "ok — harness will start another turn immediately after this one", + Err(e) => { + tracing::warn!(error = %e, path = %sentinel.display(), "request_next_turn: write failed"); + return format!("request_next_turn failed: {e}"); + } + } + .to_string() + }) + .await + } } #[tool_handler(