Skip to content

cmux NEXT agent pane: a message the person types always goes, queued or as a steer - #24039

Closed
teamleaderleo wants to merge 10 commits into
feat-cmux-nextfrom
leo/composer-send-is-person
Closed

teamleaderleo wants to merge 10 commits into
feat-cmux-nextfrom
leo/composer-send-is-person

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

What broke

Leo typed a message into a Claude chat's composer while a turn ran, and it came back under its bubble as "Not sent: it needs your own key press or click", with the earlier steer's "Send now" above it. His own typed message was refused as if a script had sent it.

Root cause: the host relay (AgentPaneTransport) counts the person's gesture as one slot. Every key press or click in the pane refreshes that slot, and a session/prompt frame uses it up when the relay decides the frame. The relay decides page frames one at a time, after an off-main parse and a hop back to the main actor. The composer frees itself at once (#22742), so a fast typist can send a second steer, or queue a prompt behind the turn, before the relay has decided the first frame. Then the first frame spends the only gesture, and the second frame finds none and is refused with transport.gesture_required. Steers and queued prompts are not replayed by the page or the host. acpmux runs the frame it was given, so nothing later adds a gesture back.

The existing per-prompt ticket (transport.gesture with {method: "session/prompt", params: {promptId}}, used for prompts held for the trust answer) could not cover this case:

  • one ticket slot per connection, so a newer prompt's reserve revoked the older prompt's ticket;
  • every ticket was cleared as soon as any prompt went out.

Fix

  • Page (direct.ts send): a send into a shown chat reserves a ticket for its own promptId right away, before any await, while the person's key press is still fresh. The frame carries it as _meta.cmuxGesture.
    • A send that already has a ticket (a held or seeded prompt) uses that ticket.
    • A chat that is still starting is left to its start credit, so a remote session/new still gets the Enter's gesture.
    • With no gesture (posted input, a script) the host gives no ticket, and the frame is decided exactly as before.
  • Host:
    • a session/prompt ticket gets its own slot per promptId (AgentPaneGestureIntent.slot);
    • a prompt going out now clears only the other kinds of ticket (clearTicketsKeepingPrompts), so other prompts' tickets stay;
    • a connection holds at most 8 prompt tickets (the newest, in issue order), so a page cannot store up one ticket per key press.

The person proof is not weakened:

  • A ticket is still minted only by consuming a real person gesture (AgentPaneUserGestures.reserve).
  • It is single-use and bound to one promptId on one connection.
  • It expires after heldPromptLifetime.
  • One gesture still authorizes at most one prompt. A prompt with no gesture and no ticket is refused, as before.

Tests

  • AgentPaneTypedPromptTicketTests (CmuxNextAgentPaneTests), at the relay:
    • two prompts typed behind a running turn, both checked 45 s later, both go;
    • a steer typed right after a queued prompt, checked first, and then the queued prompt also go;
    • a ticket sends only its own promptId, only once; a prompt with no gesture is refused; a reserve with no gesture gets no ticket.
  • typedPromptGesture.test.ts (webviews): a running prompt, a queued prompt and a steer each reserve their own ticket at send time and carry it. A kept ticket is used as is. Input that is not the person's goes without a ticket.

Fleet evidence: see the review comment (red on the test commit b8c1115, green at the head).

Observed / Not run

  • Observed: the fleet runs listed in the review comment; prepush-gate.py has no new violations.
  • Not run: no live reproduction in the app (Leo's running app was not driven). Root cause found by reading the relay and page code, plus Leo's chat log, which shows bursts of steers seconds apart.

No capture: no visible UI change. The fix is in the relay's gesture check and the page's send path, and the tests cover it at those layers.

Changelog

Fixed: A message you type in an agent chat while a turn runs, queued or sent as a steer, is no longer refused as "Not sent: it needs your own key press or click".

Agent session: cosmic-gray

🤖 Generated with Claude Code

teamleaderleo and others added 2 commits October 21, 2026 18:37
…eer keeps its own gesture

Red on base: two Enters before the relay checks either frame leave the second
with no gesture (a newer session/prompt ticket revokes the older, and the first
prompt going out clears the rest), and the page sends no per-prompt ticket.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d41-f5e8-7631-b4aa-a544e3ef96a8&host=cosmic-gray
…a steer

The host counted one gesture at a time and checked a prompt's frame only when
its serial relay got to it. Two sends before the first frame was checked (a
steer typed right after another, a prompt queued behind a running turn) left
the second with nothing: "Not sent: it needs your own key press or click".

Each send into a shown chat now binds its own gesture to its promptId at once
(transport.gesture); the frame carries that ticket however late the relay
checks it. A prompt's ticket has its own slot (a newer Enter no longer revokes
it) and survives another prompt going out. A ticket still sends only its own
promptId, once; a prompt with no gesture of the person's is refused as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d41-f5e8-7631-b4aa-a544e3ef96a8&host=cosmic-gray
@teamleaderleo teamleaderleo added the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 22, 2026
@teamleaderleo

teamleaderleo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Owned by nx-chat on big-red · took over from session 01a15d41

@blacksmith-sh

blacksmith-sh Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Found 31 test failures on Blacksmith runners:

Failures

Test View Logs
---/--- View Logs
---/--- View Logs
---/--- View Logs
---/--- View Logs
---/--- View Logs
/tmp/tmp4vvhwadr/summary-fail.json (live):/summary-fail.json (live): View Logs
/tmp/tmpdtei7xlr/summary-missing.json (live):/summary-missing.json (live): View Logs
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows/
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows
View Logs
[new_workspaces] the window draws no frames/[new_workspaces] the window draws no frames View Logs
[tabs] debug.tab_switch timed 0 of 40 switches/
[tabs] debug.tab_switch timed 0 of 40 switches
View Logs

...and 21 more test failures. View all on Blacksmith: run 39865687662, run 39865687704, run 39865687691

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

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 2c25394: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr24039 -f ref=leo/composer-send-is-person -f platform=macos -f backend_mode=local

Probably yours: transcript-steer-continuous.test.tsx fails here but not on main's latest full suite. Seen on other PRs too (likely flaky): agent-pane-no-capsules.test.ts, agent-pane-peer-chat.test.ts, transcript-copy.test.tsx and 1 more.

CI failed on 2c25394198 (run 39865687704 attempt 1): 1 code, 1 unknown.

Job Verdict Why
web / react-apps-check (1) code a web test failed
web / react-apps-check unknown no known signature; failed step: Check webview test shards

Failures:

  • seen on other PRs agent-pane-no-capsules.test.ts agent pane page > rounds its controls as small-radius rects, not capsules: failed (job)
  • seen on other PRs agent-pane-peer-chat.test.ts a chat on an SSH machine > counts its running turn from the prompt and offers no machine switch mid-...: failed (job)
  • seen on other PRs transcript-copy.test.tsx a sent prompt's row has exactly one Copy, the one under its bubble; tool and answer rows unchanged: failed (job)
  • seen on other PRs transcript-range-content.test.tsx button range copy includes a finished answer once: failed (job)
  • new in this PR transcript-steer-continuous.test.tsx a Send now whose request the dropped socket fails still continues the turn: timed out after 30000ms (job)
Matched log lines
web / react-apps-check (1): (fail) agent pane page > rounds its controls as small-radius rects, not capsules [54.14ms]

Not re-run automatically: web / react-apps-check (1), web / react-apps-check are not machine failures.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

red: cmux-next checks (god files, concurrency, crash safety, l10n); no finished base run on 84d5d32 to compare; do not merge until green; see lane-rules
red: web / Web status; base red: the same failure on the base 84d5d32; does not block
red: web / react-apps-check; base red: the same failure on the base 84d5d32; does not block
red: web / react-apps-check (1); NEW: Expected length: 1 Received length: 0 at copyRange (/home/runner/_work/cmux/cmux/webviews/test/transcript-range-content.test.tsx:103:20) at async <anonymous> (/ (+4 more) is not red on the base 84d5d32; do not merge until green; see lane-rules

Gallery before/after for 2c25394: 16 states rendered, 3 images differ, 1591 more touched states not rendered. Images are in the run's artifact, not on this public PR.

gh run download 39865687504 -R manaflow-ai/cmux -n gallery-advisory-24039-2c2539419888707f6f8e1b86a3fc6c2d972e5a70 (then read gallery-review.json)

States
State Theme Status Diff Picked by
agent-pane.chat-width/standard Apple System Colors Light unchanged 0% own App.tsx
agent-pane.chat-width/standard Apple System Colors unchanged 0% own App.tsx
agent-pane.chat-width/wide Apple System Colors Light unchanged 0% own App.tsx
agent-pane.chat-width/wide Apple System Colors unchanged 0% own App.tsx
agent-pane.chat-width/wide-blocks Apple System Colors Light unchanged 0% own App.tsx
agent-pane.chat-width/wide-blocks Apple System Colors unchanged 0% own App.tsx
agent-pane.composer-floats/scrolled-to-end Apple System Colors Light unchanged 0% own App.tsx
agent-pane.composer-floats/scrolled-to-end Apple System Colors unchanged 0% own App.tsx
agent-pane.composer-floats/composer-grown Apple System Colors Light unchanged 0% own App.tsx
agent-pane.composer-floats/composer-grown Apple System Colors unchanged 0% own App.tsx
agent-pane.docked-chat/new-chat Apple System Colors Light unchanged 0% own App.tsx
agent-pane.docked-chat/new-chat Apple System Colors unchanged 0% own App.tsx
agent-pane.docked-chat/after-turn Apple System Colors Light unchanged 0% own App.tsx
agent-pane.docked-chat/after-turn Apple System Colors unchanged 0% own App.tsx
agent-pane.docked-chat/working Apple System Colors Light unchanged 0% own App.tsx
agent-pane.docked-chat/working Apple System Colors unchanged 0% own App.tsx
agent-pane.docked-chat/typing-in-dock Apple System Colors Light unchanged 0% own App.tsx
agent-pane.docked-chat/typing-in-dock Apple System Colors unchanged 0% own App.tsx
agent-pane.docked-chat/long-title Apple System Colors Light unchanged 0% own App.tsx
agent-pane.docked-chat/long-title Apple System Colors unchanged 0% own App.tsx
agent-pane.orchestration-lab/local-queue Apple System Colors Light unchanged 0% own App.tsx
agent-pane.orchestration-lab/local-queue Apple System Colors unchanged 0% own App.tsx
agent-pane.orchestration-lab/build-box Apple System Colors Light unchanged 0% own App.tsx
agent-pane.orchestration-lab/build-box Apple System Colors unchanged 0% own App.tsx
agent-pane.orchestration-lab/cloud-fair-share Apple System Colors Light unchanged 0% own App.tsx
agent-pane.orchestration-lab/cloud-fair-share Apple System Colors unchanged 0% own App.tsx
agent-pane.streaming-follow-motion/stream Apple System Colors Light unchanged 0% own App.tsx
agent-pane.streaming-follow-motion/stream Apple System Colors unchanged 0% own App.tsx
agent-pane.streaming-text/claude Apple System Colors Light changed 0% own App.tsx
agent-pane.streaming-text/claude Apple System Colors changed 0% own App.tsx
agent-pane.streaming-text/codex Apple System Colors changed 0% own App.tsx
agent-pane.streaming-text/codex Apple System Colors Light unchanged 0% own App.tsx

Pick other states with a Gallery: entry.id, entry.id/variant line in the PR body.

teamleaderleo and others added 2 commits October 21, 2026 19:36
Review: with a slot per prompt, a page could bank one ticket per key press
for ten minutes. Keep the newest eight per connection (issue order), name the
clear for what it keeps, and say in direct.ts where a send can wait first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d41-f5e8-7631-b4aa-a544e3ef96a8&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-vpnq4 created for this PR by the cmux coordinator.

Codex review: with a hard cap of eight, a ninth fast Enter revoked the first
prompt's ticket before the relay got to it. The cap now drops only tickets
older than a live gesture (a banked one, or one waiting on a trust answer),
so any number of prompts typed in a burst all go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d41-f5e8-7631-b4aa-a544e3ef96a8&host=cosmic-gray
@cursor

cursor Bot commented Oct 22, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next crash-class rules / cmux-next crash-class rules is red on 13 of 13 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

Codex review: a page that reserves on every key press and never sends kept
every fresh ticket. A connection now holds at most 64 prompt tickets in all
(the oldest go first); the cap of 8 on stale ones stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d41-f5e8-7631-b4aa-a544e3ef96a8&host=cosmic-gray
Codex review: App.tsx's send awaits the chat's start and persistence before
client.send, and the composer is free meanwhile, so two Enters could still
share one gesture. The send now binds its gesture first (bindSendGesture) and
passes the pending ticket on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d41-f5e8-7631-b4aa-a544e3ef96a8&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex xhigh via acpmux, correctness first): SHIP at 23f1405

A Codex reviewer (gpt-6.1-sol, xhigh, read-only, via acpmux on cmux16) did four rounds. Each CHANGES finding was fixed before the next round:

  1. A hard cap of 8 dropped the first of nine fast sends. Now only tickets older than a live gesture are evicted past 8.
  2. Fresh tickets had no upper limit. Now each connection holds at most 64.
  3. App.tsx awaited the session start and persist before client.send. The composer now binds its Enter first (bindSendGesture).
  4. SHIP at this exact head: no correctness or security findings. The person-proof guarantee holds: a ticket comes only from a real gesture of the person, it is bound to one promptId on one connection, and it can be used once.

An earlier Claude subagent review also said SHIP, at ee52d42 and at ebf50b4.

Evidence:

  • cmux-ci compile-check --app at 23f1405 passed in 59 s. It also passed at ee52d42, ebf50b4, 77aebfc and 77c0b74.
  • Fleet test steps are still queued on the light-class backlog and have no result yet. The first round of runs, at b8c1115 and ee52d42, failed before tests ran because base was red at the time: the agent-pane CSS bundle check failed on ContextSection.css, and later on wave.css.
    • Red: 55ef2c0965fe7ce4ab5875d4 (Swift) and 2cab950fb80a58ca10ac418b (web), at the tests-only commit 51872b0.
    • Green: 7bc5a8f2f4e0c105973eeed5 (Swift, at 77c0b74) and ce8aaf834c956cb5ec914a53 (web, at this head).
  • PR CI's own test jobs run on this head.

…r-send-is-person

# Conflicts:
#	webviews/src/agent-session/acpmux/direct.ts
@cursor

cursor Bot commented Oct 22, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex xhigh via acpmux, correctness first): SHIP at 6afa7a2

Codex (xhigh, acpmux on cmux16, read-only from gh pr diff and git refs) round 5 at this head, after the base merge: no correctness or security findings. The seven-file diff contains only this PR's changes. One wording nit: the comment at AgentPaneUserGestures.swift:109 says fresh tickets "always stay", but the 64-ticket cap still applies to them. That's left as is. Rounds 1 to 3 asked for changes and all were fixed: stale-only eviction, a cap of 64 fresh tickets, and the gesture bound before await ensureSession. Round 4 gave SHIP at 23f1405.

Person-proof still holds. One gesture gives one ticket, scoped to its promptId and connection, single use, with a lifetime, and the tickets per connection are capped. A script or agent with no key press or click gets no ticket, same as on base.

Evidence at this head:

  • cmux-ci compile-check --app passed (80 s).
  • The fleet red/green test steps are still queued on the light pool: Swift red 55ef2c0965fe7ce4ab5875d4 and web red 2cab950fb80a58ca10ac418b at the tests-only commit 51872b0, Swift green 7bc5a8f2f4e0c105973eeed5, web green ce8aaf834c956cb5ec914a53.
  • The PR's reds are not this PR's own:

@cursor

cursor Bot commented Oct 22, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Orphaned: no activity for 14 h, and no lane holds a lease on it. Adopted by the orphan sweep: merged feat-cmux-next in at 2c25394. A later pass queues it for the merge hub if it is reviewed, small and red only on base reds.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Stale (13d, conflicts); rebuild fresh from feat-cmux-next if still wanted

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Superseded by #25154 (commit 36b8f27 agent send/steer delivery); this stale PR is already closed.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 2c253941 Deployed Oct 22, 2026 by teamleaderleo via namespace autofix #1523
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:high Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant