fix(#1021): error on unauthorized target (not silent ignore); allow child targeting without cap

This commit is contained in:
damocles 2026-06-01 21:26:42 +02:00
commit d35b7ab9b4
3 changed files with 44 additions and 38 deletions

View file

@ -372,22 +372,20 @@ pub enum Request {
#[serde(default)]
file_path: Option<String>,
},
/// Loose-ends view. On the agent socket, scoped to the calling agent
/// unless the agent holds the `query_agent_state` capability, in
/// which case `Some("<name>")` targets a specific agent's threads
/// (the `"*"` hive-wide value is manager-only). On the manager
/// socket, `agent = None` scopes to the manager itself,
/// `Some("*")` is hive-wide, `Some("<name>")` is that agent's
/// loose ends. See `docs/conventions.md::Loose-ends wire shape`.
/// Loose-ends view. On the agent socket: `None` = self; direct
/// children are always accessible; non-children require the
/// `query_agent_state` capability — rejected with an error otherwise;
/// `"*"` is always rejected (use the manager socket). On the manager
/// socket: `None` = manager self, `"*"` = hive-wide, any name =
/// that agent. See `docs/conventions.md::Loose-ends wire shape`.
GetLooseEnds {
#[serde(default, skip_serializing_if = "Option::is_none")]
agent: Option<String>,
},
/// Count of pending (un-delivered) reminders. On the agent socket,
/// scoped to the calling agent unless the agent holds
/// `query_agent_state`, in which case `Some("<name>")` targets
/// that agent. On the manager socket, `agent = None` means self,
/// `Some("<name>")` means that agent.
/// Count of pending (un-delivered) reminders. On the agent socket:
/// same target rules as `GetLooseEnds` (self/children free;
/// non-children require `query_agent_state`; `"*"` rejected).
/// On the manager socket: `None` = self, any name = that agent.
/// Used by the harness's per-turn stats sink.
CountPendingReminders {
#[serde(default, skip_serializing_if = "Option::is_none")]
@ -395,11 +393,10 @@ pub enum Request {
},
/// Reminder statistics: counts of scheduled, delivered, and pending
/// reminders over a time window. `since_secs` filters to reminders
/// created in the last N seconds (0 = all). On the agent socket,
/// scoped to the calling agent unless the agent holds
/// `query_agent_state`, in which case `Some("<name>")` targets
/// that agent. On the manager socket, `agent = None` means self,
/// `Some("<name>")` means that agent.
/// created in the last N seconds (0 = all). On the agent socket:
/// same target rules as `GetLooseEnds` (self/children free;
/// non-children require `query_agent_state`; `"*"` rejected).
/// On the manager socket: `None` = self, any name = that agent.
ReminderRollup {
/// Only count reminders created in the last N seconds from now.
/// Pass 0 to include all reminders.
@ -917,13 +914,13 @@ pub enum Capability {
/// MCP tool `get_host_journal` is only registered in the harness
/// when this capability is present.
ReadHostJournal,
/// Agent can query another agent's state via `GetLooseEnds`,
/// Agent can query non-child agents via `GetLooseEnds`,
/// `CountPendingReminders`, and `ReminderRollup` on the agent
/// socket. Without this capability the `agent` field in those
/// requests is ignored and results are scoped to the caller.
/// The `"*"` hive-wide value is not available on the agent socket
/// even with this capability — use the manager socket for swarm-wide
/// scans.
/// socket. Without this capability, targeting a non-child agent is
/// rejected with an error (direct children are always accessible
/// without any capability). The `"*"` hive-wide value is not
/// available on the agent socket even with this capability — use the
/// manager socket for swarm-wide scans.
QueryAgentState,
}