claude: pipe prompt via stdin (variadic --allowedTools was eating it); + ManagerRequest::Status
This commit is contained in:
parent
9eab28a716
commit
accb1445e3
5 changed files with 34 additions and 10 deletions
|
|
@ -9,7 +9,7 @@ use hive_ag3nt::events::{Bus, LiveEvent};
|
||||||
use hive_ag3nt::login::{self, LoginState};
|
use hive_ag3nt::login::{self, LoginState};
|
||||||
use hive_ag3nt::{DEFAULT_SOCKET, DEFAULT_WEB_PORT, client, mcp, web_ui};
|
use hive_ag3nt::{DEFAULT_SOCKET, DEFAULT_WEB_PORT, client, mcp, web_ui};
|
||||||
use hive_sh4re::{AgentRequest, AgentResponse};
|
use hive_sh4re::{AgentRequest, AgentResponse};
|
||||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[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 hyperhive MCP surface auto-approved. Bash pattern allow-list is on
|
||||||
/// the backlog (CLAUDE.md).
|
/// the backlog (CLAUDE.md).
|
||||||
async fn run_turn(prompt: &str, mcp_config: &Path, bus: &Bus) -> Result<()> {
|
async fn run_turn(prompt: &str, mcp_config: &Path, bus: &Bus) -> Result<()> {
|
||||||
|
// Don't pass the prompt as a positional arg: `--allowedTools <tools...>`
|
||||||
|
// and `--tools <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")
|
let mut child = Command::new("claude")
|
||||||
.arg("--print")
|
.arg("--print")
|
||||||
.arg("--verbose")
|
.arg("--verbose")
|
||||||
|
|
@ -224,11 +229,16 @@ async fn run_turn(prompt: &str, mcp_config: &Path, bus: &Bus) -> Result<()> {
|
||||||
.arg(mcp::builtin_tools_arg())
|
.arg(mcp::builtin_tools_arg())
|
||||||
.arg("--allowedTools")
|
.arg("--allowedTools")
|
||||||
.arg(mcp::allowed_tools_arg())
|
.arg(mcp::allowed_tools_arg())
|
||||||
.arg(prompt)
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.spawn()?;
|
.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 stdout = child.stdout.take().expect("piped stdout");
|
||||||
let stderr = child.stderr.take().expect("piped stderr");
|
let stderr = child.stderr.take().expect("piped stderr");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -147,8 +147,8 @@ async fn serve(socket: &Path, interval: Duration) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(ManagerResponse::Empty) => {}
|
Ok(ManagerResponse::Empty) => {}
|
||||||
Ok(ManagerResponse::Ok) => {
|
Ok(ManagerResponse::Ok | ManagerResponse::Status { .. }) => {
|
||||||
tracing::warn!("recv produced Ok (unexpected)");
|
tracing::warn!("recv produced unexpected response kind");
|
||||||
}
|
}
|
||||||
Ok(ManagerResponse::Err { message }) => {
|
Ok(ManagerResponse::Err { message }) => {
|
||||||
tracing::warn!(%message, "recv error");
|
tracing::warn!(%message, "recv error");
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,20 @@
|
||||||
//! Embedded MCP server. Claude Code (running inside the agent container)
|
//! Embedded MCP server. Claude Code (running inside the agent container)
|
||||||
//! launches this as a stdio child via `--mcp-config`; tool calls land here
|
//! launches this as a stdio child via `--mcp-config`; tool calls land here
|
||||||
//! and are translated to `AgentRequest::Send`/`Recv` against hyperhive's
|
//! and are translated to `AgentRequest::*` / `ManagerRequest::*` against
|
||||||
//! own per-agent unix socket at `/run/hive/mcp.sock`.
|
//! hyperhive's own per-container unix socket at `/run/hive/mcp.sock`.
|
||||||
//!
|
//!
|
||||||
//! Two protocols, two surfaces:
|
//! Two protocols, two surfaces:
|
||||||
//! - **hyperhive socket** at `/run/hive/mcp.sock` — JSON-line, our
|
//! - **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.
|
//! - **MCP stdio** owned by this module — what claude actually speaks.
|
||||||
//!
|
//!
|
||||||
//! The agent surface today is intentionally tiny (send/recv); the manager
|
//! Two server flavors:
|
||||||
//! surface (Phase 8 follow-up) will add `request_spawn`, `request_kill`,
|
//! - `AgentServer` — sub-agent tools (`send`, `recv`).
|
||||||
//! `request_apply_commit`.
|
//! - `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;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,12 @@ async fn dispatch(req: &ManagerRequest, coord: &Coordinator) -> ManagerResponse
|
||||||
message: format!("{e:#}"),
|
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) {
|
ManagerRequest::Recv => match coord.broker.recv(MANAGER_AGENT) {
|
||||||
Ok(Some(msg)) => ManagerResponse::Message {
|
Ok(Some(msg)) => ManagerResponse::Message {
|
||||||
from: msg.from,
|
from: msg.from,
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,9 @@ pub enum ManagerRequest {
|
||||||
body: String,
|
body: String,
|
||||||
},
|
},
|
||||||
Recv,
|
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
|
/// 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
|
/// creates and starts the container. Brand-new agent names only — if an
|
||||||
/// agent of the same name already exists, the approval will fail.
|
/// agent of the same name already exists, the approval will fail.
|
||||||
|
|
@ -234,4 +237,5 @@ pub enum ManagerResponse {
|
||||||
Err { message: String },
|
Err { message: String },
|
||||||
Message { from: String, body: String },
|
Message { from: String, body: String },
|
||||||
Empty,
|
Empty,
|
||||||
|
Status { unread: u64 },
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue