agent session attach: typing, view and presence for a chat of another machine (chat multiplayer)
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}andagent-session-view {surface, visible}. They call acpmux's_acpmux/typingand_acpmux/viewon the tab's own session, and name no session. acpmux's_acpmux/presencefor that session streams as{event:"agent-session-presence", surface, presence}. The capability isagent-session-presence-v1, advertised withagent-session-attach-v1. The event goes only to a connection that echoed the capability inset-client-info, because an older app ends its attachment on anagent-session-*event it does not know. acpmux'styping.rate_limitedanswersagent_session.limit. An observation attachment may sendagent-session-viewbut notagent-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 inset-client-infoonly when the daemon lists it;AgentSessionRemoteClient.typing/viewand the.presenceevent;RemoteAcpmuxWireserves_acpmux/typingand_acpmux/viewfor its own session and forwards_acpmux/presencefor that session only. An observer never types.- The pane's method allowlist takes
_acpmux/typing {sessionId, on}._acpmux/viewwas 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:falseand 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_verbnow 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