From accb1445e346ab64d0af073c2da7cdeaffda145e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?m=C3=BCde?= Date: Fri, 15 May 2026 15:06:09 +0200 Subject: [PATCH] claude: pipe prompt via stdin (variadic --allowedTools was eating it); + ManagerRequest::Status --- hive-ag3nt/src/bin/hive-ag3nt.rs | 14 ++++++++++++-- hive-ag3nt/src/bin/hive-m1nd.rs | 4 ++-- hive-ag3nt/src/mcp.rs | 16 ++++++++++------ hive-c0re/src/manager_server.rs | 6 ++++++ hive-sh4re/src/lib.rs | 4 ++++ 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/hive-ag3nt/src/bin/hive-ag3nt.rs b/hive-ag3nt/src/bin/hive-ag3nt.rs index 3273253..a4c03b5 100644 --- a/hive-ag3nt/src/bin/hive-ag3nt.rs +++ b/hive-ag3nt/src/bin/hive-ag3nt.rs @@ -9,7 +9,7 @@ use hive_ag3nt::events::{Bus, LiveEvent}; use hive_ag3nt::login::{self, LoginState}; use hive_ag3nt::{DEFAULT_SOCKET, DEFAULT_WEB_PORT, client, mcp, web_ui}; use hive_sh4re::{AgentRequest, AgentResponse}; -use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::process::Command; #[derive(Parser)] @@ -211,6 +211,11 @@ fn format_wake_prompt(label: &str, from: &str, body: &str) -> String { /// the hyperhive MCP surface auto-approved. Bash pattern allow-list is on /// the backlog (CLAUDE.md). async fn run_turn(prompt: &str, mcp_config: &Path, bus: &Bus) -> Result<()> { + // Don't pass the prompt as a positional arg: `--allowedTools ` + // and `--tools ` are variadic in claude-code, and the + // trailing positional gets swallowed into one of them — claude then + // errors with "Input must be provided either through stdin or as a + // prompt argument when using --print". Pipe via stdin instead. let mut child = Command::new("claude") .arg("--print") .arg("--verbose") @@ -224,11 +229,16 @@ async fn run_turn(prompt: &str, mcp_config: &Path, bus: &Bus) -> Result<()> { .arg(mcp::builtin_tools_arg()) .arg("--allowedTools") .arg(mcp::allowed_tools_arg()) - .arg(prompt) + .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn()?; + if let Some(mut stdin) = child.stdin.take() { + stdin.write_all(prompt.as_bytes()).await?; + stdin.shutdown().await.ok(); + drop(stdin); // signal EOF to claude + } let stdout = child.stdout.take().expect("piped stdout"); let stderr = child.stderr.take().expect("piped stderr"); diff --git a/hive-ag3nt/src/bin/hive-m1nd.rs b/hive-ag3nt/src/bin/hive-m1nd.rs index 0afb5fc..7244099 100644 --- a/hive-ag3nt/src/bin/hive-m1nd.rs +++ b/hive-ag3nt/src/bin/hive-m1nd.rs @@ -147,8 +147,8 @@ async fn serve(socket: &Path, interval: Duration) -> Result<()> { } } Ok(ManagerResponse::Empty) => {} - Ok(ManagerResponse::Ok) => { - tracing::warn!("recv produced Ok (unexpected)"); + Ok(ManagerResponse::Ok | ManagerResponse::Status { .. }) => { + tracing::warn!("recv produced unexpected response kind"); } Ok(ManagerResponse::Err { message }) => { tracing::warn!(%message, "recv error"); diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index 264d1c3..26acb29 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -1,16 +1,20 @@ //! Embedded MCP server. Claude Code (running inside the agent container) //! launches this as a stdio child via `--mcp-config`; tool calls land here -//! and are translated to `AgentRequest::Send`/`Recv` against hyperhive's -//! own per-agent unix socket at `/run/hive/mcp.sock`. +//! and are translated to `AgentRequest::*` / `ManagerRequest::*` against +//! hyperhive's own per-container unix socket at `/run/hive/mcp.sock`. //! //! Two protocols, two surfaces: //! - **hyperhive socket** at `/run/hive/mcp.sock` — JSON-line, our -//! broker-routed Send/Recv. Unaffected by this module. +//! broker-routed protocol. Unaffected by this module. //! - **MCP stdio** owned by this module — what claude actually speaks. //! -//! The agent surface today is intentionally tiny (send/recv); the manager -//! surface (Phase 8 follow-up) will add `request_spawn`, `request_kill`, -//! `request_apply_commit`. +//! Two server flavors: +//! - `AgentServer` — sub-agent tools (`send`, `recv`). +//! - `ManagerServer` — agent tools + lifecycle (`request_spawn`, `kill`, +//! `request_apply_commit`). +//! +//! Both go through the same `run_tool_envelope` helper so logging + status +//! line stay uniform. use std::path::PathBuf; diff --git a/hive-c0re/src/manager_server.rs b/hive-c0re/src/manager_server.rs index a7329da..c474eb6 100644 --- a/hive-c0re/src/manager_server.rs +++ b/hive-c0re/src/manager_server.rs @@ -81,6 +81,12 @@ async fn dispatch(req: &ManagerRequest, coord: &Coordinator) -> ManagerResponse message: format!("{e:#}"), }, }, + ManagerRequest::Status => match coord.broker.count_pending(MANAGER_AGENT) { + Ok(unread) => ManagerResponse::Status { unread }, + Err(e) => ManagerResponse::Err { + message: format!("{e:#}"), + }, + }, ManagerRequest::Recv => match coord.broker.recv(MANAGER_AGENT) { Ok(Some(msg)) => ManagerResponse::Message { from: msg.from, diff --git a/hive-sh4re/src/lib.rs b/hive-sh4re/src/lib.rs index b6a5dc3..efb6585 100644 --- a/hive-sh4re/src/lib.rs +++ b/hive-sh4re/src/lib.rs @@ -208,6 +208,9 @@ pub enum ManagerRequest { body: String, }, Recv, + /// Non-mutating: pending message count, used to render a status line + /// after each MCP tool call (mirrors `AgentRequest::Status`). + Status, /// Submit a spawn request for the user to approve. On approval the host /// creates and starts the container. Brand-new agent names only — if an /// agent of the same name already exists, the approval will fail. @@ -234,4 +237,5 @@ pub enum ManagerResponse { Err { message: String }, Message { from: String, body: String }, Empty, + Status { unread: u64 }, }