Guest page: watch a shared agent chat in any browser (carries #28837)

Merged
TE
5d ago
+3194-66

Leo: "also what about the group chats for inviting people to see my sessions?" and "can this be a proper feat inside acpmux/the multiplayer and team situation?"

Carries #28837 (Guest page: watch a shared agent chat in any browser, itself a carry of #26779) onto current feat-cmux-next. #28837's commits are kept as they were, same author, plus one merge of feat-cmux-next. #28837 had gone conflicting with base and its lane has been idle since 2026-10-31 06:28 UTC.

The share op it pairs with (cmux share chat, Share Chat with Guest) already landed as #27959; this is the browser side a guest opens from that link.

Merge resolution (the only lines not from #28837)

  • acpmux/App.tsx: base dropped text= from ReceivedActions; the read-only guard from #28837 stays around it (readOnly ? undefined : <ReceivedActions row more />).
  • acpmux/chips/ReplyImage.tsx: base added an early return for template paths; useTranscriptReadOnly() is called before it, so the hook order is the same on every render, and the import list keeps both isTemplatePath and useTranscriptReadOnly.
  • acpmux/Localizable.xcstrings: base's text kept byte for byte, with #28837's 18 guest.* keys inserted unchanged.

Security

The page is a client only. Access is decided by the room (the daemon's chat-share redeem and guest socket, already on base): the secret comes from the URL fragment, is removed from the address bar at once, is kept in sessionStorage only until redeem, and the guest socket authenticates with the room's cookie. Room frames are validated before they reach the transcript reducer. A read-only transcript never loads the owner's local files (ReplyImage shows a chip) and makes no host calls (guest-no-host-calls.test.tsx).

No Chief code.

Agent session: agent session

🤖 Generated with Claude Code


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


Summary by cubic

Carries #28837 onto feat-cmux-next: a standalone page where a guest of a shared agent chat watches the transcript live in any browser, no install.

  • Adds join, live, and ended states, fed by the room's cmux.share/1 socket (snapshot first, seq dedupe, reconnect with backoff, end/kicked); a fed client draws what it is fed until disposed, and room frames are validated before reaching the transcript reducer.
  • Imposes a read-only transcript boundary: no row or control draws anything that acts on the owner's machine or daemon, and no hook asks the owner's host (GuestActionsBoundary, readOnly.ts, guestHost.ts); guest-no-host-calls.test.tsx spies on every host route and expects zero calls.
  • The share secret is read from the URL fragment, removed from the address bar at once, and kept in sessionStorage only until redeem.
  • Adds build-guest-page-web.sh and vite.config.guest-page.mjs so the hosted room can serve the bundle at /g/<share_id>.
  • Merge resolutions stay limited to App.tsx, ReplyImage.tsx, and Localizable.xcstrings; the latest merge resolved only App.tsx. ShellRow now draws no Stop or Open without a shell-actions context, so its test provides the owner's actions and exercises the read-only row without them.