agents: remove send(operator) and the operator inbox; alert on turn failures
Agents reach the operator over matrix now (#5115). A `send` to `operator` is refused, by the broker and by the MCP allow-list check, with an error that names the matrix MCP tools. The dashboard's Y3R C4LL tab goes with it: the operator inbox, `GET /api/operator-inbox`, `Broker::unread_for_recipient`, and the browser notifications that only ever fired on operator-bound messages. The harness no longer posts turn failures and plugin-install failures to the operator inbox. Both are already logged at warn; a new Grafana rule, `hh-agent-turn-failed`, fires per agent on the harness's `turn failed` line. Dashboard-to-agent messaging (op-send, the agent web UI's message box, mark-all-read for agent inboxes) is unchanged. Decisions per mara on #5115: q1 a, q2 a plus a grafana alert, q3 keep dashboard-to-agent messaging and leave swarm-level code alone.
This commit is contained in:
parent
2bf809cbe5
commit
7e6a4c7727
37 changed files with 246 additions and 848 deletions
|
|
@ -491,7 +491,7 @@ fn handle_status(coord: &Arc<Coordinator>, agent: &str) -> hive_core_agent_sock:
|
|||
}
|
||||
|
||||
/// `OperatorMsg` — deliver an operator-authored message into `agent`'s
|
||||
/// inbox (from the `operator` recipient).
|
||||
/// inbox (sender `operator`).
|
||||
fn handle_operator_msg(
|
||||
coord: &Arc<Coordinator>,
|
||||
agent: &str,
|
||||
|
|
@ -603,12 +603,11 @@ pub(crate) fn handle_send(
|
|||
}
|
||||
};
|
||||
}
|
||||
// Validate that the recipient is a known local agent or the
|
||||
// special "operator" recipient. Without this check a typo in `to`
|
||||
// silently queues a message nobody will ever read.
|
||||
// Validate that the recipient is a known local agent. Without this
|
||||
// check a typo in `to` silently queues a message nobody will ever read.
|
||||
//
|
||||
// Cross-hive messaging (`name@hive` qualified names) is not routed
|
||||
// through the broker — use the Matrix MCP tools for that instead.
|
||||
// Cross-hive messaging (`name@hive` qualified names) and the operator
|
||||
// are not reached through the broker — the Matrix MCP tools are.
|
||||
if to.contains('@') {
|
||||
return Response::Err {
|
||||
message: format!(
|
||||
|
|
@ -617,20 +616,23 @@ pub(crate) fn handle_send(
|
|||
),
|
||||
};
|
||||
}
|
||||
if to != hive_sh4re::manager::OPERATOR_RECIPIENT {
|
||||
// A name that doesn't parse as an Ident can't be a local agent, so
|
||||
// it collapses into the same "unknown recipient" error as a valid
|
||||
// name with no state dir.
|
||||
let exists = hive_types::Ident::parse(to)
|
||||
.is_ok_and(|id| crate::paths::agent_state_dir(&id).exists());
|
||||
if !exists {
|
||||
return Response::Err {
|
||||
message: format!(
|
||||
"send failed: unknown recipient `{to}` \
|
||||
(no agent with that name exists on this hive)"
|
||||
),
|
||||
};
|
||||
}
|
||||
if to == hive_sh4re::manager::OPERATOR_RECIPIENT {
|
||||
return Response::Err {
|
||||
message: hive_sh4re::manager::OPERATOR_SEND_REFUSAL.to_owned(),
|
||||
};
|
||||
}
|
||||
// A name that doesn't parse as an Ident can't be a local agent, so
|
||||
// it collapses into the same "unknown recipient" error as a valid
|
||||
// name with no state dir.
|
||||
let exists =
|
||||
hive_types::Ident::parse(to).is_ok_and(|id| crate::paths::agent_state_dir(&id).exists());
|
||||
if !exists {
|
||||
return Response::Err {
|
||||
message: format!(
|
||||
"send failed: unknown recipient `{to}` \
|
||||
(no agent with that name exists on this hive)"
|
||||
),
|
||||
};
|
||||
}
|
||||
match coord.broker.send(&Message {
|
||||
from: hive_sh4re::manager::trusted_sender(agent),
|
||||
|
|
@ -664,6 +666,22 @@ mod tests {
|
|||
(dir, Arc::new(coord))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_to_operator_is_refused_with_the_matrix_pointer() {
|
||||
let (_dir, coord) = coordinator();
|
||||
let resp = handle_send(
|
||||
&coord,
|
||||
"iris",
|
||||
hive_sh4re::manager::OPERATOR_RECIPIENT,
|
||||
"hi",
|
||||
None,
|
||||
);
|
||||
assert!(
|
||||
matches!(&resp, Response::Err { message } if message.contains("mcp__matrix__")),
|
||||
"{resp:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn over_long_line_is_rejected_after_reading_only_the_bound() {
|
||||
let total = 2 * REQUEST_LINE_MAX_BYTES as u64;
|
||||
|
|
|
|||
Loading…
Reference in a new issue