chief: agents' messages carry their sender and take the ask floor; tell(session:NAME) as the Chief

Merged
TE
5d ago
+986-41

Problem

Agents that message the Chief through cmux chief -p, cmux chief send or cmux chief acp were posted as the owner (user_local). A lane's message read as the user's own words and ran under the owner's approve-all policy. The Chief also had no way to answer or message another acpmux session as itself.

Task (gaps 1 and 2 of the Chief-vs-acpmux gap study): "messages from other agents arriving via cmux chief -p / cmux chief acp must carry sender identity/class/thread and show as that agent, not as the owner (user_local), and must not inherit the owner's approve-all policy", plus "an optchat tool that sends to an acpmux session as the Chief, plus a read-only session list, with replies routed back into the Chief chat the way subagent reports are".

Change

Inbound (gap 1)

  • cmux_conversation::agent_sender is a new codec. A message carries its sender in its client_msg_id as agent~NAME~CLASS~THREAD~NONCE, and its text shows [from NAME] …. This needs no new daemon op. The marker can only lower trust.
  • The CLI sets the sender on every send path:
    • cmux chief -p takes --from NAME, --class C and --thread T. Without --from, it uses ACPMUX_SESSION_NAME.
    • cmux chief send uses ACPMUX_SESSION_NAME.
    • cmux chief acp reads the new _meta.acpmux.sender.
  • acpmux now passes another agent's name, class and thread to the agent as _meta.acpmux.sender, on turn prompts and on steers.
  • cmux_chief::rules::is_owner_message_as returns false for a marked message. The turn therefore runs under the ask floor, and its children inherit it. The Chief reads the message labelled with its session, class and thread, plus a note that it is not the user's word.

Outbound (gap 2)

  • tell(id: "session:NAME", message) sends to acpmux session NAME as the Chief (fromAgent: chief, class question). NAME's answer comes back as a [session:NAME] … report. It is a new Source::Agent, which requires approval, the same way a subagent's report arrives.
  • control(action: "sessions") lists the sessions the Chief may tell. It is read-only and allowed in ask turns.
  • These are refused as targets: any Chief's own turn and compactor sessions, and this Chief's subagents (tell those by a<N>).
  • An ask turn may only tell the sessions whose messages it carries (turn_agents). So another agent or person cannot use the Chief to start a talk with a third session.

The README policy table, the tool descriptions and the golden tool list are updated.

Tests

  • Red first: another_agents_message_through_the_cli_is_that_agents_and_takes_the_ask_floor (approval_gates.rs) was committed failing in 65c8a04, then fixed.
  • Unit tests:
    • the codec round trip and its cleaning limits;
    • CLI --from and the env fallback;
    • the chief acp sender meta;
    • acpmux prompt_sender;
    • session_tell naming, refusals and the reply label.
  • Gate checks: an ask turn may tell its own sender, is refused for another session, and may run control sessions. A tell to an unknown session says so.

Risk

This is Chief-area code (optchat-chief brain, chief-kernel tool list), so it lands with the Chief-area checks. acpmux now adds an _meta.acpmux.sender key to session/prompt for agent-sent prompts; ACP _meta is ignored by harnesses that do not read it. The Cloud Chief wasm (backend/.../optchat-wasm) is not rebuilt here and keeps the old tool descriptions (follow-up).

šŸ¤– Generated with Claude Code

Agent session: agent session


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Messages from other agents sent through cmux chief now carry the sending session's identity instead of posting as the owner, and the Chief can message other acpmux sessions as itself with replies routed back like reports.

Inbound attribution

  • Agent-sent messages are marked in client_msg_id (agent~NAME~CLASS~THREAD~NONCE), show as [from NAME], and can only ever lower trust, never grant owner status. The marker is honoured only on the owner's own messages: a forged marker from another person names nobody, so their turn gets no session to tell.
  • Their turns run under the ask floor instead of the owner's approve-all policy; --from/--class/--thread on cmux chief -p, ACPMUX_SESSION_NAME for send, and _meta.acpmux.sender for acp set the sender. acpmux also passes the sender meta on the steers it sends to a running agent.

Outbound messaging

  • tell(id: "session:NAME") sends to an acpmux session as the Chief; the answer returns as a [session:NAME] report under the ask floor. A tell steered into a running turn waits for that turn's end, and the route is dropped after the answer.
  • control action: "sessions" lists reachable sessions; Chief's own turn/compactor sessions, its subagents, and Chief-harness chats are refused, and an ask turn may only tell sessions whose messages it carries.