agent session attach: typing, view and presence for a chat of another machine (chat multiplayer)

Merged
TE
11d ago
+491-9

Leo's words, for the chat multiplayer work:

  • "we can still do the regular chat multiplayer" (2026-10-26)
  • "also chief chats we need multiplayer"
  • "we can just send some codexes to do whatever"
  • "for the agent chat side, we should be able to just... do stuff. unless lawrence is super actively workin on stuf rn"

What

Contract item 5 of the chat multiplayer design (hq#4389): presence and typing reach a chat whose acpmux runs on another machine. acpmux's side landed in #25285 and #25358 (_acpmux/typing, _acpmux/view, _acpmux/presence).

  • Daemon (cmux-tui): two new verbs on an attached tab, agent-session-typing {surface, on} and agent-session-view {surface, visible}. They call acpmux's _acpmux/typing and _acpmux/view on the tab's own session, and name no session. acpmux's _acpmux/presence for that session streams as {event:"agent-session-presence", surface, presence}. The capability is agent-session-presence-v1, advertised with agent-session-attach-v1. The event goes only to a connection that echoed the capability in set-client-info, because an older app ends its attachment on an agent-session-* event it does not know. acpmux's typing.rate_limited answers agent_session.limit. An observation attachment may send agent-session-view but not agent-session-typing (agent_session.read_only). The verbs are documented in spec/commands.md.
  • App (Swift):
    • DaemonCapability.agentSessionPresence, plus its entries in daemon-capabilities.json and check-daemon-capabilities.sh;
    • AgentSessionAttachClient.typing/view, which do nothing on a daemon without the capability. The client echoes the capability in set-client-info only when the daemon lists it;
    • AgentSessionRemoteClient.typing/view and the .presence event;
    • RemoteAcpmuxWire serves _acpmux/typing and _acpmux/view for its own session and forwards _acpmux/presence for that session only. An observer never types.
    • The pane's method allowlist takes _acpmux/typing {sessionId, on}. _acpmux/view was already allowed.

No UI change in this PR. The page side (presenceClient.ts, which sends typing and draws the faces) is #25303.

No capture: this is wire only (daemon verbs, the Swift attach client and the remote acpmux bridge). It draws no pixel: the faces and typing line it carries draw in #25253, and the two-person capture over cmux host share follows once the pieces are on the base.

Not here

A teammate admitted by cmux host share (#25300) is not covered yet. A follow-up after #25300 lands will:

  • stamp the bound author on these two calls;
  • add them to the proxy's verb list;
  • attach a teammate with visible:false and then send an authored view, so acpmux counts the teammate and not the owner;
  • name the teammate's own id in the attach page, so their pane leaves itself out of the faces.

Risk

Low. These are additive verbs on the existing trusted-local attach path. They carry two booleans and nothing else (every other param is agent_session.bad_request), and acpmux bounds typing at 2 calls a second.

Tests

Rust, through cmux-ci test (Linux): cargo fmt clean, cargo test -p cmux-tui-core --lib agent_session 58 passed, clippy clean. New tests:

  • typing_and_view_go_to_the_pinned_session_and_presence_streams_back: exact acpmux params, bad params refused, the capability advertised, and only this session's presence streamed;
  • typing_needs_an_attachment;
  • presence_reaches_only_a_connection_that_asked_for_it: a connection that did not echo the capability gets the next record but no presence event;
  • the rate case: acpmux's third typing call in a second answers agent_session.limit;
  • the observe test: typing is refused read-only, and a view goes through;
  • every_verb now includes both verbs, so the untrusted-connection and relay-denial tests cover them.

Swift (CI): AgentSessionAttachClientPresenceTests runs over a real line socket. A daemon that lists the capability gets the echo and both verbs. One that does not gets neither verb and no echo. RemoteAcpmuxPresenceTests checks the wire forwards typing and view for its own session only, refuses another session or a missing flag, forwards only this chat's presence, and that an observer never types.

Agent session: agent session

šŸ¤– Generated with Claude Code