Chief: trusted agent sessions may tell and spawn its own subagents
When another agent session sends the Chief a message through cmux chief (a coordinator routing work, for example), the Chief's turn takes the ask floor. That floor refused tell and spawn to the Chief's own subagents with "refused: this turn carries another person's text". So the Chief could not pass on a coordinator's routing to its own subagents, even when the owner wanted it to.
Leo, 2026-11-02, on that refusal: "that seems kinda lame? why shouldn't you be able to do that? i think you should."
What changes
- New Chief settings:
relay.trustAgents(defaulttrue).relay.trustedAgents(default["*-coord"]). This is a list of acpmux session names, each an exact name or a pattern with one*. A bare*is refused.- The configured
contactsession is trusted too. - Set with
optchat-chief settings set relay.trustedAgents "a-coord, lead-*"or... relay.trustAgents false. Both work with the host running or stopped.
- A turn whose only other-person text comes from trusted sessions may:
tellany of the Chief's own subagents. The subagent sees the message prefixed with[relayed from NAME].spawnsubagents with the owner's policy, instead of the ask floor.
- Everything else stays refused in that turn, and its own session keeps policy
ask. That covers:- Bash and file writes
telltosession:orchief:idsremember,cloud_workspace,watchcommands and attachmentsacpmux_send
- The relay is dropped, and the whole floor applies, when:
- any message in the turn is untrusted, whether it opened the turn or was steered in;
- an agent's message is steered into an owner's turn;
- a turn is resumed after a restart.
- The turn's framing line names the sender. It says what the turn may do, and that the Chief should not ask the user for more access.
Trust model. A sender's name is read only from a message the owner's own CLI posted (agent_sender_as). A teammate's forged marker names nobody, and another_persons_forged_agent_marker_names_nobody still passes. Anything that can post as the owner could already post unmarked owner text. So the list is a convenience scope, not a new boundary.
Tests. Red first: the test commit fails on the old host. These pass with the fix:
tests/approval_gates.rs:- a trusted coordinator may tell and spawn, and the tell shows the sender, while Bash, sessions, Chiefs, memory, Cloud, watch and attachments stay refused;
- an untrusted agent in the same turn keeps the whole floor;
- the switch turns it off;
- the list and the contact decide who is trusted.
- Unit tests in
chief_settings.rscover matching, parsing and the round trip. - The full
optchat-chieftest suite and clippy (-D warnings) pass on the Linux pool.
Risk. Chief code (access policy). The change opens only tell and spawn to the Chief's own subagents, and only for owner-trusted sessions. The person can turn it off.
Follow-ups (from review):
- tests for a mid-turn steer of an untrusted message, a late tell after a relay turn, and a resumed relay turn;
- a
contactmatch that also checks the host; - show the sender on a spawned task too.
Agent session: agent session
🤖 Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Allows the Chief to relay trusted agent message turns to its own subagents: when only trusted sessions' text opened the turn, tell and spawn are allowed, with the tell prefixed [relayed from NAME] and spawn running under the owner's policy. Previously such turns refused both as "this turn carries another person's text". Everything else—Bash, file writes, sessions, Chiefs, memory, Cloud, watch, attachments, acpmux_send—stays refused, and an untrusted message in the same turn drops the relay.
The trust scope is configured with two new settings, relay.trustAgents (default true) and relay.trustedAgents (default ["*-coord"], plus the configured contact session); a bare * is refused.
Trust model
- Sender names come only from messages the owner's own CLI posted, so a forged teammate marker names nobody and the list is a convenience scope, not a new boundary.