Expand description
Fleet control — select, broadcast, and bulk-act on many agents at once. See docs/specs/SPEC_MULTI_AGENT_FLEET_CONTROL_2026_08_20.md.
fleet.broadcast/fleet.bulk-stop are thin server-side loops over the
EXISTING single-target primitives (ReactiveHandler::inject_message,
agent_io::stop_one_agent_block) — no new transport is invented, and
every action returns FleetActionResult (succeeded/failed per target),
never a single bool, per the spec’s §3 finding that silent partial
failure is the single most commonly-cited fleet-ops UX failure mode.
fleet.broadcast is deliberately WS-RPC-only (the human/Swarm-UI path),
not exposed over HTTP to agentmux-mcp. It is NOT a jekt: a broadcast is the
human typing once to several panes, so each target gets a message routed the
way an inter-agent message is (bootstrap::route_agent_message: persistent, ACP
and App Server on their own channel, a subprocess or unspawned agent via
run_agent_turn) with a one-line [BROADCAST:FROM=user VIA=swarm ...] header
from reactive::broadcast_turn_message, instead of a self-declared jekt from
unknown. See docs/specs/SPEC_SWARM_BROADCAST_AS_USER_MESSAGE_2026_10_01.md.
The turn is BROADCAST_TURN_ORIGIN (User): a broadcast is the human’s own
message, with the same powers as typing it into each pane, including
authorizing an agent’s self-quit (owner decision 2026-10-06, spec §6 q2).
That adds no power to anything holding the pane key, which could already send
any pane a plain User turn through agent.input (spec §2 finding 7, §4.2).
srv, not the text, still decides: the label comes from this module only.
A target running on another channel on this machine is reached over loopback,
the way a stop is (forward_broadcast_to_channel): the sender passes only the
target block, the body, the MSGID and the RECIPIENTS count, and the
receiving instance builds the header and starts the turn itself
(deliver_forwarded_broadcast). Agents on other machines are not reachable
this way: nothing on the LAN proves who is asking
(docs/specs/SPEC_SWARM_REMOTE_AGENTS_PLATFORM_TAG_AND_SELECTION_2026_10_03.md §6).
The forward uses the same loopback auth as the stop forward, and reaches only
what that channel’s own fleet.broadcast already does.
An AGENT-initiated broadcast instead loops the EXISTING signed single-target
SendMessage MCP tool path client-side (see agentmux-mcp’s FleetBroadcast
tool) — only the calling agent’s own process holds its AGENTMUX_JEKT_KEY, so
per-message signing can only happen there, never in a server-side batch RPC.
fleet.bulk-stop has no such constraint (a controller stop involves no
jekt signing at all, same as agent.stop today) and IS exposed over
HTTP (POST /api/v1/fleet/bulk-stop) for FleetBulkStop.
Structs§
- Broadcast
Forward 🔒 - What a forwarded broadcast carries. No header text: the receiving instance writes the header, so a caller on loopback cannot supply its own.
- Broadcast
Outcome 🔒 - What happened to one target of a broadcast.
Enums§
- Broadcast
Action 🔒 - What to do with one target, given how its controller routes a message.
- Broadcast
Route 🔒 - How one broadcast target is reached.
- Forwarded 🔒
- What asking another instance’s user came to.
Constants§
- BROADCAST_
CONCURRENCY 🔒 - Turns started at once. Delivery is not rate-limited the way
inject_messagewas (which is why the old 10-per-second chunking is gone), but a pane’s turn can spawn a CLI, so a broadcast to a large selection is bounded rather than starting every turn in the same instant. - BROADCAST_
TURN_ 🔒ORIGIN - Every broadcast turn is attributed here, and only here:
User— see the module doc. A test pins both this value and thatrun_broadcasthands it to the delivery function. It wasAutomateduntil 2026-10-06. - FLEET_
BROADCAST_ 🔒AUDIT_ ACTION - FLEET_
BULK_ 🔒STOP_ AUDIT_ ACTION - NO_
REGISTERED_ 🔒AGENT
Functions§
- broadcast_
action 🔒 - Maps
route_agent_message’s answer to an action. A PTY-based target is refused rather than typed into: a broadcast is prose, and keystrokes into a shell prompt would run it as a command. - deliver_
broadcast_ 🔒turn - Delivers one broadcast text to one block the way an inter-agent message reaches
it (
route_agent_message_from): structured controllers (persistent, ACP, App Server) take it on their own channel, which also steers a turn already running; a subprocess or not-yet-spawned persistent agent gets a turn started withorigin. Either way the turn is labelled withoriginwhere the controller tracks provenance at all (persistent and subprocess, the same agents a typed message can authorize a self-quit on). - deliver_
forwarded_ 🔒broadcast - The receiving end of
forward_broadcast_to_channel: builds the header here, from the target’s own agent id, and delivers it as any broadcast turn (BROADCAST_TURN_ORIGIN). Audited on this side too, naming where it came from. - exceeds_
fail_ 🔒threshold batch_failures / batch_len(as a percentage) exceedsmax_pct. Cross-multiplies instead of computing a truncated integer percentage first —batch_failures * 100 / batch_lenrounds DOWN before comparing, so e.g. 1 failure in a batch of 3 (33.3%) reads as 33 and never exceedsmax_pct = 33, silently missing the threshold it was meant to catch (Codex P2, PR #2687 review).batch_failures * 100 > max_pct * batch_lenis the same inequality with no rounding.- fleet_
broadcast_ 🔒impl - fleet_
bulk_ 🔒stop_ impl - fleet_
bulk_ 🔒stop_ with_ override FleetBulkStopfrom an agent (the HTTP path; the Swarm UI’s ownfleet.bulk-stopis the user and stops at once): every target running on this instance waits for its user’s 15 s override, all windows in parallel (docs/specs/SPEC_AGENT_SELF_QUIT_2026_09_24.md §6.5). A target running on another instance on this machine is asked there, in that instance’s own window (forward_stop_pending). Targets running nowhere go throughfleet_bulk_stop_implas before,stagedincluded.- forward_
broadcast_ 🔒to_ channel - Asks the instance at
local_url(another channel on this machine, itsauth_keyfrom the shared registry, same user) to deliver one broadcast. A channel from before the route answers 404 and is named as such, for that target only. - forward_
stop_ 🔒pending - A cross-channel
FleetBulkStoptarget: ask ITS instance’s user (/agentmux/agent/stop-pending), whose banner, chime and Keep running are the ones that user sees. The request’s status is served from there;pending_shutdown::remember_remotelets this instance proxy it. An instance without that route (404) gets the plain forward it always did. - forward_
stop_ 🔒to_ shared_ channel - Stops
targets(block ids) via the existing single-targetstop_one_agent_block, one call per target, and — unlike that single-target primitive, which involves no jekt signing and so was never audited — records oneAuditLogEntryper target viaReactiveHandler::log_fleet_action_audit, so Warden’s Audit tab sees every fleet-initiated stop (SPEC_MULTI_AGENT_FLEET_CONTROL_2026_08_20.md§6; reagent/Codex P2, PR #2687 review — this was missing entirely). Withoutstaged, runs every target as a single batch (still returns full per-target detail, never a bool). Withstaged, targets are stoppedbatch_sizeat a time; if the failure rate WITHIN a completed batch exceedsmax_fail_percentage, remaining targets are recorded as failed (untried) andaborted_earlyis set — caps blast radius on a bad selection rather than plowing through every remaining target.aborted_earlyis only ever set when targets were genuinely left unattempted — tripping the threshold on the LAST batch (nothing left to skip) must not report “aborted early” when the full list actually ran (reagent P2, same review). Look upblock_idin the shared cross-channel registry (this host, other channels — same registryserver/reactive.rs’s inject cascade tier-2b already reads) and forward a stop request over loopback HTTP to that channel’s own srv (/agentmux/agent/stop), using its ownauth_keyfrom the registry entry — identical trust model to the inject cascade’s own cross-channel forward (same host, same user). - register
- register_
fleet_ 🔒broadcast - register_
fleet_ 🔒bulk_ stop - register_
fleet_ 🔒group_ create - register_
fleet_ 🔒group_ delete - register_
fleet_ 🔒group_ list - register_
fleet_ 🔒group_ update - run_
broadcast 🔒 - The broadcast logic, with srv’s two touch points passed in so it can be tested
without an
AppState:resolvemaps a block id to its agent id, anddeliverstarts one turn (block id, full text, origin). - shared_
channel_ 🔒for - The shared cross-channel registry entry for
block_idon ANOTHER instance on this machine: loopback only, never this instance itself.