agent socket: external wake-up path for in-container MCP servers

new AgentRequest::Wake { from, body } drops a message into
this agent's inbox via the per-agent socket. matrix-style MCP
servers can use it when they receive an external event
(matrix message, webhook, scrape result) to nudge claude
into running a turn. broker.send wakes whatever Recv is
currently long-polling, the harness picks the message up,
formats a wake prompt with the caller's chosen from label
('matrix: new dm', 'webhook: deploy succeeded', etc.).

new `hive-ag3nt wake --from <label> --body <text>` subcommand
on the harness binary so MCP servers can shell out instead of
implementing the line-JSON protocol themselves; body=='-'
reads from stdin for multi-line / quoting-friendly payloads.

identity = socket: anything that can connect to /run/hive/mcp
.sock is implicitly trusted to inject. that's fine because the
bind-mount is the agent's own container; no new auth surface
opens up.

docs/turn-loop.md gets a new 'Waking the agent from inside
the container' section pointing at both paths (CLI + raw
JSON).
This commit is contained in:
müde 2026-05-16 03:15:58 +02:00
parent 96cb9f84c9
commit 90df2106bf
4 changed files with 79 additions and 0 deletions

View file

@ -33,6 +33,19 @@ enum Cmd {
/// `--mcp-config`; tools dispatch through `/run/hive/mcp.sock` back into
/// the hyperhive broker.
Mcp,
/// Inject a wake-up event into this agent's inbox so the next turn
/// fires with the given body. Intended for extra MCP servers /
/// helpers running inside the container (matrix bridge, scraper,
/// webhook listener) that need to nudge claude on external events.
/// `from` is the sender label that appears in the wake prompt
/// (claude sees "from: matrix" etc.).
Wake {
#[arg(long)]
from: String,
/// Body of the wake message. Pass `-` to read from stdin.
#[arg(long)]
body: String,
},
}
#[tokio::main]
@ -94,6 +107,25 @@ async fn main() -> Result<()> {
}
}
Cmd::Mcp => mcp::serve_agent_stdio(cli.socket).await,
Cmd::Wake { from, body } => {
// Read body from stdin if caller passed `-`. Same convention
// many CLI tools use; keeps multi-line / shell-quoting
// friction out of the body content.
let body = if body == "-" {
let mut buf = String::new();
std::io::Read::read_to_string(&mut std::io::stdin(), &mut buf)?;
buf
} else {
body
};
let resp: AgentResponse =
client::request(&cli.socket, &AgentRequest::Wake { from, body }).await?;
match resp {
AgentResponse::Ok => Ok(()),
AgentResponse::Err { message } => anyhow::bail!("wake: {message}"),
other => anyhow::bail!("wake: unexpected response {other:?}"),
}
}
}
}