Skip to content

fix(agent-pane): authenticate CEF question Return targets - #26813

Closed
teamleaderleo wants to merge 8 commits into
feat-cmux-nextfrom
nx-cef-question-key-1028
Closed

teamleaderleo wants to merge 8 commits into
feat-cmux-nextfrom
nx-cef-question-key-1028

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

The CEF question-card keyboard repair needs a target-specific proof. The current page input callback carries only uptime and the page's main world can call it; a composer Return must never become an answer key for a pending question.

Add an authenticated, isolated key-target channel … bound to the native pending question, so only a real user key on the pending question card is accepted.

This is the CmuxNextPages transport prerequisite for the native ledger/person-key repair #26820. #26739 is visual-only and supplies no authorization contract. It uses the existing CEF DevTools/isolated-world primitives; it changes no browser shim, daemon protocol, AgentPane ledger, web attributes, or gesture policy. It does not claim that the unwired full question repair works today.

The native API is:

bindQuestionKeyTargets(_ targets: [PageQuestionKeyTarget],
                       received: (PageQuestionKeySnapshot) -> Void) -> Bool
bindQuestionKeyTarget(_ target: PageQuestionKeyTarget?,
                      received: (PageQuestionKeySnapshot) -> Void) -> Bool
redeemQuestionKeySnapshot(_ snapshot: PageQuestionKeySnapshot) async -> Bool

PageQuestionKeyTarget names a native-approved session and permission. The collection contains every live pending question, so a visible oldest card need not be the first dictionary entry. Unchanged membership is idempotent; changing it revokes unused proofs. The original trusted key event's target/path chooses one approved card. Neither a page request nor a fresh later selector chooses the target.

PageQuestionKeySnapshot exposes immutable target and replyJSON only, with no public initializer. Its private issuer/ticket binds it to this page, channel, generation and key serial. replyJSON is the exact {optionId, answers, annotations?} captured at key time; annotations absent and {} remain distinct. No session or permission override is allowed in it. Harness-specific canonical answers/annotations remain the native question owner's policy.

The isolated closure keeps the nonce and original fieldset/Other textarea references private. Its binding exists only in the returned main-frame execution context. Native delivery checks that context, nonce, approved target and increasing per-key serial, retaining at most one unused snapshot. Redemption consumes the native ticket before awaiting and checks the same captured references, focus, labels, field text and raw reply in the same isolated context. The native owner then rechecks its pending decision nonce/person-instance and spends its matching fresh person-key witness. Generic input uptime and a later page callback grant nothing.

Integration DOM contract:

  • Fieldset .acpmux-question carries data-cmux-question-session, data-cmux-question-id and data-cmux-question-enter-reply.
  • Original focused target is .acpmux-question-other[data-row-editing="true"] textarea.
  • Keep that target and payload stable until snapshot redemption; only then commit answer UI/send state. Never put the nonce/ticket in DOM or the page main world.

The private focus guard is registered and awaited during agent-page setup with Page.addScriptToEvaluateOnNewDocument(worldName:) before page.load, without runImmediately. Its first window capture precedes page listeners, so an earlier-than-binding page stopImmediatePropagation handler cannot hide focus leave+return. Bind setup, key capture and redemption all require its private focus generation and liveness witness. The liveness probe uses a separate unguessable event type and closure-private acknowledgment; it never changes focus. A document.open reset that retains the private JS global but clears its early listeners fails closed, as does an already-loaded document with no early guard. Nothing late-installs that guard. Its constant-time listener belongs to the document/page; per-snapshot observers/listeners end with their ticket.

Lifecycle: focus leave (including leave+return), input, label/reply mutation (including change+restore), detach/re-attach, target collection change, context replacement, navigation, hiding, retarget, crash and close revoke unused proof. Listeners and the observer are removed on revocation. A new genuine key may capture a fresh snapshot on the same pending card; empty/incomplete Enter never permanently disarms it. Binding may precede card mount. The observer watches only the bound ancestor chain and named attributes, without a subtree/row scan; record processing is bounded. No timer or polling loop is added. Async setup checks a generation after each suspension; late old setup/cleanup cannot replace a newer listener.

Validation at d6b7cb618e3ddc02a96aa58fb7d1a7ec25fdfd61:

  • Added focused Swift/JavaScriptCore regressions for main-world calls even with a guessed nonce, composer Return, synthetic/repeat/IME/modified keys, replay, empty→edit→Return, cross-page tickets, delayed focus/payload/id/editing/detach changes restored before redeem, second approved target, unknown target, unchanged allowlist, late DOM mount, context death and stale setup ordering. Added hostile page window-capture focus ABA, unarmed-document refusal, document.open listener-reset and real host registration-before-load cases. These are not yet executed receipts.
  • The original native submission timed out without a receipt. A later reconciled submission at this exact head requested Release arm64 compile a8cf0869912888dffb4b870d and focused CmuxNextPagesTests 0621102409c707f9946cc095. Both requests were accepted; native execution has no terminal verdict yet. Their supported waits retain the same IDs through controller read timeouts; no duplicate request follows a timeout.
  • The test-first commit cannot execute on base because the channel API does not exist there; it is not presented as a behavioral red receipt.
  • Real-Chromium/CDP run 40669299936 is SUCCESS at this exact head/tree (receipt 895cdee9ab6b46f396429c1a765339b4). It executes the exact extracted Swift JS templates, source SHA-256 32cc87e6f52d185b5e43f8ecca28010ecba34190773dbb223bb2da7c9a63c543. Trusted Enter → hostile page capture-suppressed focus ABA rejects the old snapshot and accepts a fresh Enter. Payload, identity and detach ABA reject then recover; document.open listener reset rejects old tickets and mints no new proof; an unarmed existing document refuses binding, while a freshly secured document works. Main-world binding calls and ordinary composer Return produce no proof. This is Chromium/CDP script evidence, not a native CEF app launch or Swift compile.
  • git diff --check passes. Review (Codex Astra, high) is SHIP at d6b7cb618e3ddc02a96aa58fb7d1a7ec25fdfd61. Primary independently inspected the final source and exact hosted result. Native compile/tests remain required and unverified. No local heavy checks ran.

Agent session: agent session

Adds behavioral channel tests before the new host API. Base has no private channel API, so this intermediate commit is not an executable red receipt; hosted final verification will exercise the new boundary.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-66c88286003b549b3069
Bind a private DevTools function only to a named main-frame isolated context. A one-use nonce stays in that world and the native channel rejects main-world contexts, wrong secrets and replay. Trusted Return must target the focused pending question Other field; navigation, hide, page retirement and native target changes revoke the channel synchronously. The agent owner must separately match its native person-key witness and live pending identity.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-66c88286003b549b3069
An empty or incomplete Other answer keeps the question pending. Keep the isolated listener while that target stays live and authenticate strictly increasing per-key serials, each accepted once. The native owner still supplies the matching person-key witness; replaying an earlier empty Return grants nothing.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-66c88286003b549b3069
@cursor

cursor Bot commented Oct 28, 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.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 18ee299: 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=pr26813 -f ref=nx-cef-question-key-1028 -f platform=macos -f backend_mode=local

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 28, 2026
@blacksmith-sh

blacksmith-sh Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Found 4 test failures on Blacksmith runners:

Failures

Test View Logs
main.DeveloperIdAppTests.test_download_prints_the_active_profile_only/
test_download_prints_the_active_profile_only
View Logs
stubs: /System/Library/Frameworks/Security.framework/Versions/A/Security has no stub/
Security has no stub
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_CFRetain']/
CoreFoundation, missing 1: ['_CFRetain']
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_log2']/
CoreFoundation, missing 1: ['_log2']
View Logs

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

Capture an immutable reply and original card/Other-field references in the isolated key handler. A channel-private issuer ticket redeems once after checking focus, labels, reply, text and attachment state in that same context. Focus leave, payload/identity mutation and detach/re-attach revoke the old proof while a later genuine key remains usable. Keep one pending snapshot and a native-approved target collection; unchanged membership is idempotent. The agent-pane owner still supplies authoritative pending state, canonical answer policy and the native person-key witness.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-66c88286003b549b3069
Register the private main-frame focus guard as a new-document isolated-world script before the agent page loads, without runImmediately. Its earliest window capture observes focus leave despite later page stopImmediatePropagation; a separate private liveness probe rejects document.open resets that keep the JS global but clear listeners. Binding, trusted key capture and redemption all require that early guard. Existing unarmed documents fail closed, with no late reinstall. Keep the accepted collection/snapshot API and native owner boundaries.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-66c88286003b549b3069
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

@cursor

cursor Bot commented Oct 29, 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

Owned by an agent session · session

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra high, next-review): BLOCK at eb40a85

  • [P1] PageQuestionKeyTarget.swift:158: Target capture runs after page window listeners. A main-world window-capture keydown handler can relabel the original card from approved question A to approved question B before the isolated document listener runs. The channel then issues and redeems proof for B. The mutation observer starts afterward, so it cannot detect this substitution. Capture identity before page handlers execute.

  • [P1] PageQuestionKeyTarget.swift:154: Page listeners can suppress input revocation. A window-capture input handler using stopImmediatePropagation() prevents the field’s revocation listener from running. Changing and restoring the textarea value leaves no observed mutation, and redemption accepts the old snapshot despite intervening input. Input invalidation needs the same early-listener protection as focus invalidation.

Both cases reproduced using the extracted script and the PR’s DOM test model.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra high, next-review): SHIP at 31a700e

No blocking findings.

cmux-bot[bot]
cmux-bot Bot previously approved these changes Oct 29, 2026

@cmux-bot cmux-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmux-bot approves 31a700e: an independent review passed this exact head (next-review receipt by teamleaderleo). A push needs a new review; a later BLOCK at this head dismisses this approval.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Backlog lander (drain-native-app) note: the Astra SHIP is at 31a700e (after the base merge), but this PR's own test fails on the fleet at that head. ChromiumQuestionKeyChannelTests.theAgentHostAwaitsEarlyGuardRegistrationBeforeLoadingItsDocument hits #require(PageWebView(descriptor:root:routes:options:chromium:)) at :437, and the init returns nil on current base (focused run key land-pr26813-head-31a700e9). The other 12 tests in the suite pass. It needs a fix for base's PageWebView init before it can land.

@cmux-bot
cmux-bot Bot dismissed their stale review October 30, 2026 13:26

A newer verdict blocks 18ee299 (no independent SHIP at the head).

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (next-review patch-id; carried): SHIP at 18ee299

The independent SHIP at 31a700e carries to this head: the PR's own change (git diff $(git merge-base feat-cmux-next HEAD) HEAD) has the same git patch-id --verbatim (5e429a74bc1b46d9) at both heads, and no merge between them was resolved by hand. Nothing new to review. A newer BLOCK, or any change to the PR's own diff, ends the carry.

teamleaderleo added a commit that referenced this pull request Oct 31, 2026
* test(agent-pane): reject forged CEF question key reports

Adds behavioral channel tests before the new host API. Base has no private channel API, so this intermediate commit is not an executable red receipt; hosted final verification will exercise the new boundary.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-66c88286003b549b3069

* fix(agent-pane): isolate CEF question key target proofs

Bind a private DevTools function only to a named main-frame isolated context. A one-use nonce stays in that world and the native channel rejects main-world contexts, wrong secrets and replay. Trusted Return must target the focused pending question Other field; navigation, hide, page retirement and native target changes revoke the channel synchronously. The agent owner must separately match its native person-key witness and live pending identity.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-66c88286003b549b3069

* fix(agent-pane): reserve a distinct proof for every question Return

An empty or incomplete Other answer keeps the question pending. Keep the isolated listener while that target stays live and authenticate strictly increasing per-key serials, each accepted once. The native owner still supplies the matching person-key witness; replaying an earlier empty Return grants nothing.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-66c88286003b549b3069

* fix(agent-pane): bind CEF answer snapshots to original key targets

Capture an immutable reply and original card/Other-field references in the isolated key handler. A channel-private issuer ticket redeems once after checking focus, labels, reply, text and attachment state in that same context. Focus leave, payload/identity mutation and detach/re-attach revoke the old proof while a later genuine key remains usable. Keep one pending snapshot and a native-approved target collection; unchanged membership is idempotent. The agent-pane owner still supplies authoritative pending state, canonical answer policy and the native person-key witness.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-66c88286003b549b3069

* fix(agent-pane): arm question focus guard before page scripts

Register the private main-frame focus guard as a new-document isolated-world script before the agent page loads, without runImmediately. Its earliest window capture observes focus leave despite later page stopImmediatePropagation; a separate private liveness probe rejects document.open resets that keep the JS global but clear listeners. Binding, trusted key capture and redemption all require that early guard. Existing unarmed documents fail closed, with no late reinstall. Keep the accepted collection/snapshot API and native owner boundaries.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-66c88286003b549b3069

* fix(agent-pane): wire isolated question Return replies into transport

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(agent-pane): rebind question Return after visibility changes

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix: surface native question Return refusal and retry

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* test: expect the mapped invalid-intent refusal message

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(questions): preserve held Return proof and retry revoked bindings

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(questions): recognize Claude AskUserQuestion natively

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(questions): reject stale same-ID Return bindings

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(questions): recognize keypad Enter as Return

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(questions): refresh binding after Chromium context loss

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(questions): accept Claude native answer keys and reject lost Return

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(questions): revoke Return witness on ordinary grants

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* test(questions): cover captured Return admission and close races

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(questions): reject captured Return replies across transport closure

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix question key capture against page event races

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(acpmux): resolve string request ids

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325

* fix(tests): use PageWebView fixture roots

Agent-Session: https://cmux.com/deeplink/agent?id=aid-9c822a1be5e4adcfa325
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Superseded by #28129, which landed as 9c48c4a.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 18ee2990 Deployed Oct 30, 2026 by teamleaderleo via wait for same-key tree publisher #83257
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant