fix(#2190): replace MANAGER_AGENT fallbacks with "operator" for attribution
- loose_ends.rs: NULL submitter on legacy approval rows → "operator" - questions.rs: NULL submitter on cancel_loose_end → "operator" - server.rs: HostRequest::RequestSpawn submitter → "operator" - dashboard.rs: web-UI spawn submitter → "operator" - socket_server.rs: submit_init_config with no declared parent → "operator" - mcp.rs: drop MANAGER_AGENT exception from check_send_allowed; keep <parent> only
This commit is contained in:
parent
500c50745b
commit
9b2d16ae6a
6 changed files with 16 additions and 22 deletions
|
|
@ -2182,21 +2182,14 @@ const EXTRA_MCP_PATH: &str = "/etc/hyperhive/extra-mcp.json";
|
|||
const SEND_ALLOW_PATH: &str = "/etc/hyperhive/send-allow.json";
|
||||
|
||||
/// Enforce the per-agent send allow-list. Returns `Ok` when the
|
||||
/// recipient is permitted (no list configured, manager always
|
||||
/// allowed, or `to` is in the list); returns `Err(refusal)` with a
|
||||
/// claude-readable string when blocked — the harness surfaces the
|
||||
/// refusal as the tool result so claude knows the message didn't
|
||||
/// land and can react (e.g. route via the manager instead).
|
||||
/// recipient is permitted (no list configured, `<parent>` sentinel
|
||||
/// always allowed, or `to` is in the list); returns `Err(refusal)`
|
||||
/// with a claude-readable string when blocked <20><><EFBFBD> the harness surfaces
|
||||
/// the refusal as the tool result so claude knows the message didn't
|
||||
/// land and can react (e.g. route via `<parent>` instead).
|
||||
fn check_send_allowed(to: &str) -> Result<(), String> {
|
||||
if to == hive_sh4re::MANAGER_AGENT {
|
||||
// Always allow agents to talk to the manager — otherwise a
|
||||
// misconfigured allow-list could leave a sub-agent unable
|
||||
// to ask for help.
|
||||
return Ok(());
|
||||
}
|
||||
if to == hive_sh4re::PARENT_RECIPIENT {
|
||||
// Always allow `<parent>` — same escape-hatch rationale as
|
||||
// the manager exception. The allow-list constrains peer
|
||||
// Always allow `<parent>` — the allow-list constrains peer
|
||||
// chatter, not the structural reporting line; the operator
|
||||
// can rewire who the parent IS via `set_parent` without
|
||||
// having to remember to update the per-agent allow-list.
|
||||
|
|
|
|||
|
|
@ -1681,7 +1681,7 @@ async fn post_request_spawn(
|
|||
hive_sh4re::ApprovalKind::Spawn,
|
||||
"",
|
||||
None,
|
||||
hive_sh4re::MANAGER_AGENT,
|
||||
"operator",
|
||||
) {
|
||||
Ok(id) => {
|
||||
tracing::info!(%id, %name, "operator: spawn approval queued via dashboard");
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::Result;
|
||||
use hive_sh4re::{LooseEnd, MANAGER_AGENT};
|
||||
use hive_sh4re::LooseEnd;
|
||||
|
||||
use crate::coordinator::Coordinator;
|
||||
|
||||
|
|
@ -56,12 +56,12 @@ pub fn for_agent(coord: &Coordinator, agent: &str) -> Result<Vec<LooseEnd>> {
|
|||
}
|
||||
// Show each pending approval to the agent that submitted it. The
|
||||
// submitter column is NULL for rows predating it; those count as
|
||||
// the root agent's.
|
||||
// operator-initiated (no agent tracking predates the column).
|
||||
for a in coord.approvals.pending()? {
|
||||
let submitter = coord
|
||||
.approvals
|
||||
.submitter_of(a.id)?
|
||||
.unwrap_or_else(|| MANAGER_AGENT.to_owned());
|
||||
.unwrap_or_else(|| "operator".to_owned());
|
||||
if submitter != agent {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,13 +203,14 @@ pub fn handle_cancel_loose_end(
|
|||
// may withdraw it. Without the ownership check, any
|
||||
// approvals-group agent could cancel any other's approval by
|
||||
// id. A NULL submitter (legacy row predating the column) is
|
||||
// owned by the root agent.
|
||||
// treated as operator-initiated (no agent tracking predates
|
||||
// the column).
|
||||
check_can_cancel_approval(canceller)?;
|
||||
let submitter = coord
|
||||
.approvals
|
||||
.submitter_of(id)
|
||||
.map_err(|e| format!("{e:#}"))?
|
||||
.unwrap_or_else(|| hive_sh4re::MANAGER_AGENT.to_owned());
|
||||
.unwrap_or_else(|| "operator".to_owned());
|
||||
if submitter != canceller {
|
||||
return Err(format!(
|
||||
"cancel_loose_end: approval {id} was submitted by {submitter}, \
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
|
|||
hive_sh4re::ApprovalKind::Spawn,
|
||||
"",
|
||||
None,
|
||||
hive_sh4re::MANAGER_AGENT,
|
||||
"operator",
|
||||
)?;
|
||||
tracing::info!(%id, %name, "spawn approval queued");
|
||||
HostResponse::success()
|
||||
|
|
|
|||
|
|
@ -1847,8 +1847,8 @@ pub(crate) fn submit_init_config(
|
|||
description.as_deref(),
|
||||
// `parent` is the requesting agent (becomes the new child's
|
||||
// parent); it's also the submitter the approval events route
|
||||
// back to. No declared parent = operator/root path.
|
||||
parent.unwrap_or(hive_sh4re::MANAGER_AGENT),
|
||||
// back to. No declared parent = operator-initiated path.
|
||||
parent.unwrap_or("operator"),
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("queue approval row: {e:#}"))?;
|
||||
tracing::info!(%id, %name, "init_config approval queued");
|
||||
|
|
|
|||
Loading…
Reference in a new issue