Agent pane: a guest's held prompt in the pane's model (approvable only from its record)

Closed
TE
4d ago
+2253-142

HOLD: stacked on #26855 (_acpmux/queue_get, lane/mp-queue-get at 68ac4b4f6296), which sits on #26747 (acpmux held prompts). It lands after both; rebase onto feat-cmux-next once they merge, then drop this line. The card UI that uses it is #26742, stacked on this PR.

What

This is the pane model for a guest's held prompt. Leo (2026-10-28), on Theo's T3 Code "Share with guest": "we want to be having, be able to do this kind of flow for CMux multiplayer ... we can work on the agent chat multiplayer." In Theo's flow "each message they send waits for you"; the owner approves each message. Spec: leo-workspace design/chat-multiplayer.md §2 and §4.1; plan manaflow-ai/cmuxterm-hq#4766. It is split out of #26742 (which is now the card UI) to keep each PR reviewable.

THE RULE (direct.ts, Hold): a held prompt is approvable only when the pane holds that prompt's authoritative record for its current revision. That record is a queued / queue_updated record or _acpmux/queue_get. A listing (acpmux's queue list, a 200-character preview) never makes it approvable, whatever its preview looks like. The prompt's text, images, parts and revision always come from that one record. listedRevision is the newest revision acpmux has named anywhere. updating holds until the pane has the record at that revision.

  • Queue entries: awaitingOwner, revision, updating and listedRevision on QueuedPrompt, from queued, queue_updated, queue_approved and queue_declined, and from acpmux's list (which only names the revision).
  • Approve and Decline: answerHeld(promptId, approve, revision) sends _acpmux/queue_approve {sessionId, promptId, revision}, using the revision the card drew, or _acpmux/queue_decline {sessionId, promptId}.
  • Backfill: when the pane lacks a held prompt's record (an attach page that starts after it), it makes one _acpmux/queue_get for that prompt. Reads are keyed by selection generation and prompt id and cleared when the session resets; a read for a selection the pane has left is dropped. This happens only where acpmux's initialize names queueGet; without it the prompt stays updating and is never approvable.
  • Features: queueApproval and queueGet come from initialize's features. queueApproval is passed on to the snapshot.
  • Owner's own queued prompt: it still settles its sending bubble on its queued record. Only held prompts wait for queue_approved.
  • Needs you: each held prompt is a "needs you" item, only where queueApproval is served. Its one action is Review, which scrolls to the held card (#26742) and focuses it. Needs you never approves from a preview. New string queue.held.review, in all 21 locales.
  • Mock daemon: it answers queue_approve, queue_decline and queue_get, and names both features.

Tests

webviews/src/agent-session/acpmux/heldPrompts.test.tsx (model only, no card):

  • The client sends queue_approve {sessionId, promptId, revision} and queue_decline with the right ids.
  • Needs you shows only Review, gated on queueApproval. Review scrolls to the held card and focuses it.
  • The owner's queued prompt leaves no sending bubble.
  • A listed newer revision never joins held text; an edit past the 200-character preview waits for its own record.
  • A stale listing is ignored.
  • A cold attach reads the record with queue_get, with no history paging, and becomes approvable.
  • Leaving and reopening a chat in the middle of a read (A→B→A).
  • Images come from the record.
  • A listing never makes a prompt approvable.
  • Without queueGet the prompt stays updating and no queue_get is sent.

Every one of these was red before the fix it covers, in the review rounds on #26742.

Risk

risk:normal. The change is pane state plus requests that are refused unless acpmux serves them. Nothing changes for chats without guests. On its own (without #26742), a guest's prompt shows as an authored composer-queue row with no actions, and Review has no card to go to.

Changelog

none (the user-visible part is #26742)

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

Adds owner approval for guest prompts in chat multiplayer: a link guest's prompt now waits for the chat's owner to approve or decline it, on both the acpmux daemon and the agent pane.

  • New acpmux methods _acpmux/queue_approve, _acpmux/queue_decline, and _acpmux/queue_get: approve runs the prompt in its queue place at the revision the owner saw (a stale revision is refused), decline drops it, and queue_get reads a held prompt whole for the owner.
  • A prompt whose author carries approval: "owner" is queued, never steered in or stopping the running turn, and the queue runs past it until the owner answers. Queue records gain awaitingOwner and revision.
  • Held prompts are never forwarded to or moved onto a peer that does not list the queue approval feature; a held prompt and the owner's answer go only on the link that listed it.

The pane tracks each held prompt by its authoritative record: approvable only when the pane holds that record (queued/queue_updated or a queue_get read) at its current revision, never from a listing. A cold attach lacking the record reads it with one queue_get, gated on acpmux serving queueGet and never paging history via _acpmux/events.

  • Needs you lists each held prompt with a single Review action that scrolls to and focuses its card; it never approves from a preview.
  • The owner answers with Approve or Decline from the held prompt's card, naming the revision it drew.