Skip to content

Chief: import browser cookies from Chrome on request - #32983

Merged
lawrencecchen merged 3 commits into
feat-cmux-nextfrom
nx-32960-chief-cookie-import
Nov 3, 2026
Merged

lawrencecchen merged 3 commits into
feat-cmux-nextfrom
nx-32960-chief-cookie-import

Conversation

@austinywang

@austinywang austinywang commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #32960

What

A Chief can now import the user's sign-ins from another browser (Chrome, or any other browser cmux reads) when the user asks. The app's existing consent panel asks the user first.

The app action already existed (browser.importCookies, CLI cmux browser import-cookies, MCP app_browser_import_cookies). A Chief could not use it in practice:

  • Only the subagent prompt said how to import cookies. The Chief's own prompt did not, in either its MCP or CLI-launcher form.
  • The app did not receive a Chief's caller.home_chat. The consent panel would have named the Chief's temporary turn session id ("Agent chat “optchat-turn-…”") with no chat to reveal.

Changes

  • chief-kernel prompt (MCP and CLI-launcher forms): only when the user asks, run cmux browser import-cookies --from chrome[:PROFILE] --domain SITE --reason "why" from the shell with a 4-minute timeout, using the fewest sites. The CLI waits up to 210 s and the panel up to 180 s, so a shorter shell timeout would report a failure while the panel is still open. A denial imports nothing and the Chief says so. Goldens updated.
  • App: ActionInvocation.callerHomeChat is filled from the control request. CookieImportRequester names a Chief's run "Chief", and Show Agent Chat reveals the home workspace's docked chat (found the same way as ControlCallerLocation.homeChat). A CLI run that names only caller.home_chat must give a reason. TabModel.isHomeDockPage is new. CmuxNextApp lines: -1.
  • iOS parity row: the request can come from the phone's Chief chat, but Allow happens on the Mac, since the cookies live on that Mac.

Why the CLI rather than the MCP tool

The first push added app_browser_import_cookies to the Chief turn's cmux MCP allowlist. The review flagged that this reverses 1298092 / 53f549c ("Chief presets expose no Mac actions", pinned by agent_tools_tests::chief_turn_tools_contain_no_mac_app_actions). That push was reverted in 8af92c6.

An owner's Chief turn already has a shell, so the CLI gives it the same capability without widening its tool grant. If the MCP form is wanted later, it needs that policy owner's sign-off through #13742.

Trust

The consent gate does not change. Every run that is not the user's own in-app run must give a reason, and nothing is read from the other browser until the user clicks Allow on the panel. macOS's Keychain prompt follows. In a turn started by another person in a shared chat, the host refuses the shell (and every tool outside approval::turn_allowed_tools), so that turn cannot import.

Tests

  • CmuxNextAppTests.CookieImportCommandTests: requesterOfAChiefTurnIsTheChiefInHome (new), and agentsMustGiveAReason now covers a home_chat-only CLI run. On the base this suite does not compile (callerHomeChat does not exist).
  • chief-kernel render_system goldens (all six local prompt forms carry the new line; cloud-plain is unchanged) and tools.
  • Chief first-turn cost guard (scripts/ci/chief-first-turn-cost.sh) at the head.

Observed / Not run

Observed at head 8af92c6:

  • CmuxNextAppTests.CookieImportCommandTests: 11/11 passed (cmux-ci step 5d0e9e47361b194d1c687fd4, aws-m4pro-7). This also compiles the CmuxNext package.
  • chief-kernel render_system: 14/14; tools: 6/6 (rbx, lane nx-32960-kernel).
  • Chief first-turn cost guard: PASS, 21,157 of 50,000 tokens (cmux-ci step ef841ef0b3788cc5a14dfe80). The router's count_tokens returned 502, so these are the script's local estimates.
  • Dev build 32960-chief-cookie-import: cmux-ci job 19fcbbacfa2ca937388b5a7a, published.
  • Base check: the suite above cannot compile on the base (ActionInvocation.callerHomeChat does not exist there).
  • At the first head 5acc9ad: optchat-chief cloud_app_tools 11/11, harness 28/28 and approval_gates 21/21. Those files are unchanged at the final head.

Not run:

  • A live Chief turn in the tagged app, end to end: the panel showing "Agent chat “Chief”", Allow, then cookies landing in a cmux browser tab. This needs a person at the Mac to answer the panel and the Keychain prompt.
  • acpmux tests: the acpmux files are unchanged at the final head.

Changelog

  • Added: Chiefs can import your browser sign-ins (for example from Chrome) when you ask. cmux asks you to Allow first.

🤖 Generated with Claude Code

austinywang and others added 2 commits November 2, 2026 19:53
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Chief turn denied every app action of the cmux MCP server, so the app's
Import Cookies from Browser (`browser.importCookies`) never reached it, and
only the subagent prompt said how to import the user's sign-ins.

- Chief turns keep `app_browser_import_cookies` (optchat-chief
  CHIEF_CMUX_TOOLS and acpmux CHIEF_TURN_TOOLS). The app still shows the
  user its consent panel first; a turn another person started still
  refuses it (not in approval::turn_allowed_tools).
- The Chief's own prompt (MCP and CLI launcher forms) says to import only
  when the user asks, with the fewest sites and a reason.
- `caller.home_chat` now reaches the action invocation, so the consent
  panel names "Chief" and Show Agent Chat reveals Home's chat instead of
  showing the turn's temporary session id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Nov 3, 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.

@austinywang austinywang added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 8af92c6: 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=pr32983 -f ref=nx-32960-chief-cookie-import -f platform=macos -f backend_mode=local

Default changes

This pull request changes 1 default at 8af92c61c57f (what everyone gets without configuring anything):

  • tunable-source.CmuxNextApp/Handlers/OnboardingHandlers.swift:bind: "8e35a1abe09f1fbcd552959ad8599910d9903c13d313d3da2608c3fec9e29958" -> "c30ec953b17e7c612bc6274b6c52d2524522a29d80151e0f59434845aada3733"

Signed off on this commit by review (independent-subagent).

Merge receipt: merged as 5181467c939f with these defaults changed, signed off by review (independent-subagent).

Regenerate the snapshot with python3 scripts/ci/defaults_guard.py snapshot.

Shipped in:

…ions

Review of the first push: 53f549c / 1298092 keep every `app_*` tool
out of Chief turns (agent_tools_tests chief_turn_tools_contain_no_mac_app_actions),
and allowing `app_browser_import_cookies` would reverse that security
decision without its owner's sign-off. The Chief's owner turns already have
a shell, so both prompt forms now give `cmux browser import-cookies` from
the shell with a 4-minute timeout (the CLI waits 210 s for the person, the
panel 180 s), and the Chief tool allowlists stay as they were. A turn
another person started still refuses the shell, so it cannot import.

Also from the review: a Chief's Home tab is the store's home workspace
(`kind == "home"`, as ControlCallerLocation.homeChat finds it), and a CLI
run that names only `caller.home_chat` must give a reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austinywang

Copy link
Copy Markdown
Contributor Author

Review (correctness subagent)

Round 1, head 5acc9ad: FIX

  1. Blocker: cmux-tui/crates/acpmux/src/agent_tools_tests.rs chief_turn_tools_contain_no_mac_app_actions fails once app_browser_import_cookies is in CHIEF_TURN_TOOLS. This reverses 1298092 / 53f549c without the policy owner's sign-off. Resolved in 8af92c6: the allowlist change was reverted. Chief turns keep no app_* tools, and the Chief uses cmux browser import-cookies from its shell, which owner turns already have.
  2. The CLI-form prompt said "waits up to 3 minutes", but a shorter shell timeout reports a failure while the panel is still open. Resolved: both prompt forms now ask for a 4-minute timeout.
  3. Home tab detection used isAnyHome, which is broader than ControlCallerLocation.homeChat. Resolved: it now uses tab.isHomeDockPage && workspace.kind == "home".
  4. A CLI run carrying only home_chat skipped the reason. Resolved: needsReason now covers it, with a test.
  5. The panel name is the literal "Chief". Kept: it is the agent's product name. Naming which Chief, when the user has several, is a follow-up.

Round 2, head 8af92c6: SHIP

  • Owner turns: Bash is in TURN_TOOLS, under policy approve-all. The caller env (CMUX_CALLER_HOME_CHAT, CMUX_AGENT_SESSION) reaches the app.
  • Another person's turn refuses Bash (approval::turn_allowed_tools, brain/approvals.rs).
  • The goldens are byte-identical to the rendered prompt.
  • The Swift plumbing compiles, and the order the requester tries callers keeps the old cases.
  • Remaining nits, not blocking:
    • The import suggest card path (ImportSuggestionsCenter.cookieRun) doesn't carry home_chat. The Chief prompt doesn't use that path.
    • The home_chat-only CLI reason branch is defensive: a Chief's shell has no TTY and sends origin script.
    • The isHomeChat line in the App has no test of its own.

Runs

  • Red: optchat-chief cloud_app_tools a_chief_turn_keeps_the_cookie_import_tool at 30d32f4 (rbx, exit 101). That test belonged to the reverted MCP approach.
  • At 8af92c6: chief-kernel render_system 14/14 and tools 6/6 (rbx, lane nx-32960-kernel).
  • At 5acc9ad: optchat-chief cloud_app_tools 11/11, harness 28/28, approval_gates 21/21. CmuxNextAppTests.CookieImportCommandTests passed (cmux-ci step a8e2de7e0f688ac667b63888, aws-m4pro-6).
  • Dev build 32960-chief-cookie-import at 8af92c6: cmux-ci job 19fcbbacfa2ca937388b5a7a, published.
  • Pending at 8af92c6: CookieImportCommandTests (key nx32960-cookie-swift-8af92c61) and the Chief first-turn cost guard (key chief-first-turn-8af92c61).

@blacksmith-sh

blacksmith-sh Bot commented Nov 3, 2026

Copy link
Copy Markdown

Found 6 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmphg2l_534/summary-missing.json (live):/summary-missing.json (live): View Logs
/tmp/tmptlh8bi0a/summary-fail.json (live):/summary-fail.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
[tabs] the window draws no frames/[tabs] the window draws no frames View Logs

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

@austinywang austinywang added the ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds label Nov 3, 2026
@lawrencecchen

Copy link
Copy Markdown
Contributor

Review (independent-subagent): SHIP at 8af92c6

Correctness and security review of the full diff at this head (16 files, +93 -13), done independently of the earlier "## Review" comment.

Tier: risk:normal is correct. No file matches a high path in docs/ci/risk-tiers.md. The change also adds no high kind. The consent gate (CookieImportService.needsConsent, origin != .user) does not change. The Keychain reader (KeychainSafeStorage) and its entitlements do not change. There is no new delete, and no socket, relay or agent-pane allowlist change. Agents could already start this request before the PR: the subagent prompt already listed cmux browser import-cookies, and any shell can run the CLI. The Chief turn's MCP allowlist stays without Mac app actions (5acc9ad's allowlist change is reverted in 8af92c6; no optchat-chief file is in the net diff).

Consent: checked every entry point.

  • Socket/CLI/MCP/script/remote: ControlOrigin.validated accepts user only for .inProcess connections, so no socket caller can skip the panel. RegistryControlBridge.invocation defaults to .cli.
  • Import cards (ImportSuggestionsCenter.cookieRun): an agent-raised request keeps the agent's origin, so it gets the panel. Only the person's own click on the card is .user.
  • Chief turn: the CLI runs from the owner's turn shell with CMUX_CALLER_HOME_CHAT=1, and that run is .cli, so it gets the panel. needsReason is now stricter: a home_chat-only CLI run must give a reason (tested).
  • Shared chat: a turn started by another person has no shell (approval::turn_allowed_tools is the memory tools and Chief tools only), so it cannot run the CLI.
  • DEBUG debug.key can send keys, but the panel is non-activating and only Escape (Deny) has a key. Allow is a click only.
  • browser.import.enabled (on by default) turns the action off, and agents cannot change it (.privacy).

Scope and secrecy. One source profile, filtered with --domain. Cookies stay in memory between the reader and the CEF jar of the target profile. Only counts go back to the CLI, so the agent sees no cookie value. The error strings are static, or the type name only. Nothing leaves the Mac.

Keychain. SecItemCopyMatching reads Chrome's own "Chrome Safe Storage" item, and only after Allow. Chrome's ACL makes macOS show its own prompt (unless the person chose Always Allow before). The PR adds no entitlement.

Undo / data. Import has no undo, and the skill does not say it has: the undo it mentions is for clearCookies. An import can overwrite cmux-profile cookies with the same name, domain and path (this is not new). The PR deletes no user data.

Requester labeling (the app change). The order is: a tab whose session matches, then home_chat ("Chief", which reveals the store Home's home-dock tab), then the raw session, then the terminal. I checked it and it is correct. isHomeChat needs workspace.kind == "home", so a window Home reveals the store Home. That is acceptable. The OnboardingHandlers closure change does not change behavior. The defaults-guard red is a source hash on that file, not a changed default, and this receipt clears it.

Tests. The new requesterOfAChiefTurnIsTheChiefInHome test and the needsReason case cover the change. The red on the base is a compile failure (callerHomeChat does not exist there). The fleet results (11/11), the goldens (14/14) and the tools (6/6) are reported at this head. I did not run anything again.

Non-green checks on this head (none caused by this PR).

  • cmux-next checks: the red step is "No fleet host names in gallery fixtures", in webviews/src/agent-session/acpmux/HarnessStatusCard.gallery.tsx. This PR does not touch that file, and the same job is red on several open PRs. This is a base finding.
  • CmuxMobileKit: a timing flake in PushAppGroupLaunchTests (3.17 s against a 2 s limit), which is unrelated to this PR.
  • cmux-next swift test: the job has no steps (infrastructure). The fast tier was cancelled.

Follow-ups (not blocking, and not caused by this diff).

  1. CookieImportConsentPanel is a custom NSPanel. It is not CmuxDialogSpec, and it is not capture-excluded (sharingType default; it has an AX id on Allow). A local computer-use agent could see Allow and press it. I found no CUA rule that stops it from acting on cmux's own windows. Recommended: move the panel to the standard dialog host with capture exclusion, or make CUA refuse cmux's consent windows.
  2. caller.home_chat is a claim from the caller, as agent_session already was. Any local process on the socket can now appear as "Chief" and get a working Show Agent Chat. This is about the same as setting CMUX_AGENT_SESSION=Chief before this PR. Consider checking the Chief host's peer identity before the panel shows the name "Chief".
  3. "Only when the user asks" depends on the prompt, so a prompt injection can still make the Chief show the panel. The reason text and the latest message come from the agent. The person's Allow is the only real gate. This is the same as for subagents.

lawrencecchen added a commit that referenced this pull request Nov 3, 2026
… panel

The Allow/Deny panel before `cmux browser import-cookies` reads another
browser was a custom panel that screen capture showed and that any
automation could answer: an Accessibility press, a programmatic click or a
click another process posts (cmux's own Computer Use, any computer-use
tool, a script) allowed the import. A prompt-injected agent could ask and
then click Allow itself. Now, as macOS's own privacy prompts and Chrome's
permission prompts do:

- The panel is out of screen capture and screen sharing
  (sharingType = .none): an agent that reads the screen never sees Allow.
- Allow answers only the person's own click, read from the click's events
  in the panel's dispatch with the app's strict HID-system check
  (PersonInputCheck, as the join consent sheet): no Accessibility press,
  programmatic click, key, posted click or DEBUG automation allows.
- A click that begins less than a double-click interval after the panel
  shows or moves, or after another click, is not taken (Chrome's
  InputEventActivationProtector).
- Its buttons are the standard CmuxDialogButtonView: Deny with Escape,
  Show Agent Chat, and Allow with the default look and no key.
- cmux's Computer Use helper refuses any target window that keeps itself
  out of screen capture (kCGWindowSharingState none), whatever app shows it.

Deny, Escape, the window's close and the time limit still deny, by anyone.

The join consent sheet's person-click logic moves into CmuxNextDesign as
CmuxDialogPersonClick, so both prompts share one checked implementation
(the join sheet keeps its behavior: no activation delay).

Review of #32983 (issuecomment-6595849975).
@lawrencecchen

Copy link
Copy Markdown
Contributor

Follow-up 1 of the independent review (the consent panel) is in #33066. It does not block this PR.

#33066 makes Allow on the cookie import consent panel a person-only answer:

  • The panel is out of screen capture and screen sharing (sharingType = .none).
  • Only the person's own HID click on Allow allows (the strict PersonInputCheck of the join consent sheet). An Accessibility press, a programmatic click, a key or a click that another process posts does not allow.
  • A click that starts less than a double-click interval after the panel shows does not count (as Chrome permission prompts).
  • The buttons are the standard CmuxDialogButtonView.
  • The Computer Use helper refuses target windows that are out of screen capture.

Follow-up 2 (caller.home_chat is a claim from the caller, so any local process can make the panel show "Chief") is not in #33066, because home_chat is not on feat-cmux-next yet. After this PR lands, the requester label for Chief should come from the Chief host's peer identity, not from the caller field.

@cmux-bot cmux-bot Bot removed the stack-blocker label Nov 3, 2026
@lawrencecchen
lawrencecchen merged commit 5181467 into feat-cmux-next Nov 3, 2026
328 of 356 checks passed
@lawrencecchen
lawrencecchen deleted the nx-32960-chief-cookie-import branch November 3, 2026 06:27
lawrencecchen added a commit that referenced this pull request Nov 3, 2026
…nt (#32983) (#33111)

* test(defaults guard): a call owner's SHIP counts when the REST quota is spent (failing, #32983)

A private org member reads as CONTRIBUTOR to the workflow's token. The
guard then asks the REST API for their write access. On #32983 that call
got "API rate limit exceeded for installation", so the guard trusted
nobody and stayed red on a valid SHIP from lawrencecchen, a call owner.

Red on the base: python3 tests/test_defaults_guard.py fails
test_a_call_owners_receipt_ships_with_the_rest_quota_spent and
test_a_receipt_whose_lookup_failed_is_named_in_the_error.

* fix(defaults guard): trust a call owner's receipt without the REST lookup (#32983)

A private org member reads as CONTRIBUTOR to the workflow's token, so the
guard asked the REST API for each receipt author's write access. That call
spends the installation's REST quota, which other jobs share. On #32983 the
quota was spent ("API rate limit exceeded for installation"), the lookup
trusted nobody, and the guard stayed red on lawrencecchen's valid SHIP; the
merge service left the PR out as "defaults guard red".

The call owners come from the trusted base's .github/defaults-call-owners.json,
the same file that lets them approve a default outright, so their receipts
now need no lookup. Any other member is still looked up; when that lookup
fails for a reason other than 404, the error now names the receipt that
was not counted and says to re-run when the quota resets.

python3 tests/test_defaults_guard.py: red on the base (2 failures:
test_a_call_owners_receipt_ships_with_the_rest_quota_spent,
test_a_receipt_whose_lookup_failed_is_named_in_the_error), green on this
commit (67 tests OK).
lawrencecchen added a commit that referenced this pull request Nov 3, 2026
…alls reach a Tunable helper

The review of #33161 found Tunable forms the exact helper match missed.
Self.points(...) was skipped because Self is no declared type, and the
guard before #33161 caught it only by the shared-name match. Now:
- Self.helper resolves like a bare helper: the caller's own types.
- A caller's types also include the superclasses and protocols its types
  name, so a subclass reaches its base's helper and a conforming type a
  protocol extension's helper. Only the caller side takes inheritance, so a
  helper file that conforms to Sendable does not reach every Sendable type.
- A typealias of a Tunable, or an alias of such an alias, counts as a
  Tunable token (TD.number, `: TD = .number`). The guard before #33161
  missed this too.
- `class func` / `class var` no longer read as types named func / var.

feat-cmux-next tree: still 449 fingerprints, the same set, all of them in
the old 6182. #32983 and #32979 still report no tunable-source change.
python3 tests/test_defaults_guard.py: 6 subtests red on the test commit,
73 tests OK here. Not resolved: a helper reached through a generic
parameter (T.points).
lawrencecchen added a commit that referenced this pull request Nov 3, 2026
…9); an unchecked BLOCK still counts (#33161)

* test(defaults guard): a call owner's SHIP counts when the REST quota is spent (failing, #32983)

A private org member reads as CONTRIBUTOR to the workflow's token. The
guard then asks the REST API for their write access. On #32983 that call
got "API rate limit exceeded for installation", so the guard trusted
nobody and stayed red on a valid SHIP from lawrencecchen, a call owner.

Red on the base: python3 tests/test_defaults_guard.py fails
test_a_call_owners_receipt_ships_with_the_rest_quota_spent and
test_a_receipt_whose_lookup_failed_is_named_in_the_error.

* fix(defaults guard): trust a call owner's receipt without the REST lookup (#32983)

A private org member reads as CONTRIBUTOR to the workflow's token, so the
guard asked the REST API for each receipt author's write access. That call
spends the installation's REST quota, which other jobs share. On #32983 the
quota was spent ("API rate limit exceeded for installation"), the lookup
trusted nobody, and the guard stayed red on lawrencecchen's valid SHIP; the
merge service left the PR out as "defaults guard red".

The call owners come from the trusted base's .github/defaults-call-owners.json,
the same file that lets them approve a default outright, so their receipts
now need no lookup. Any other member is still looked up; when that lookup
fails for a reason other than 404, the error now names the receipt that
was not counted and says to re-run when the quota resets.

python3 tests/test_defaults_guard.py: red on the base (2 failures:
test_a_call_owners_receipt_ships_with_the_rest_quota_spent,
test_a_receipt_whose_lookup_failed_is_named_in_the_error), green on this
commit (67 tests OK).

* test(defaults guard): a shared helper name is no tunable source; an unchecked BLOCK still counts (failing)

#32983 and #32979 were flagged only because `toggle` and `make` are Tunable
helpers: any token with that name selected its declaration, and through
AppActions.bindApp every handler's `bind` (6182 tunable-source entries).

Two findings from the review of #33111: a BLOCK whose author's lookup failed
was dropped, so an owner's earlier SHIP passed; and a failed lookup at the
earlier head of a carried SHIP gave only a warning.

python3 tests/test_defaults_guard.py on this commit: 4 failures
(test_a_member_that_only_shares_a_helpers_name_is_not_a_tunable_source,
test_an_unchecked_block_after_an_owners_ship_keeps_the_guard_red,
test_an_unchecked_receipt_one_base_merge_back_is_named_in_the_error, and
test_a_receipt_whose_lookup_failed_is_named_in_the_error for its new wording).

* fix(defaults guard): match a Tunable helper by the call that reaches it; an unchecked BLOCK still counts

A Tunable helper (points, make, toggle) selected every declaration that had
a token of that name, so `registry.bind { model.toggle() }` in
AppActions.bindApp, and through it every handler's `bind`, was a
tunable-source default. That is 6182 fingerprints on feat-cmux-next. Now a
helper counts only as `Type.helper` where the helper's file declares Type, or
as a bare `helper` in a file that declares one of the helper file's types
(the same type or an extension of it). `x.helper` and `.helper` are other
members. The dependency walk does not change. On feat-cmux-next
(38369c1) the count goes from 6182 to 449, and no fingerprint is added.
On the diffs of #32983 and #32979 the old guard reports 1 and 2
tunable-source changes, and this one reports none.

Two findings from the review of #33111: a user whose write-access lookup
failed now counts for a BLOCK (never for a SHIP), so an unchecked receipt can
only keep the guard red. A failed lookup at the earlier head of a carried
SHIP is now named in the error too.

python3 tests/test_defaults_guard.py: 4 failures on the test commit, 72 tests
OK on this one. The real-default fixtures (helper, helper body, cross-file
Factory.number, symbol, external symbol, dynamic) still fail without a review.

* test(defaults guard): Self., typealias, subclass and protocol helper calls stay tunable sources (failing)

The review of #33161 found forms that reach a Tunable or its helper with
no Tunable token and no Type.helper of a type in the helper's file:
Self.points(...), a typealias of Tunable (TD.number, `: TD = .number`), a
subclass calling an inherited helper (bare or Sub.helper), and a type
calling a helper of a protocol extension it conforms to.

On this commit all 6 subtests fail. On the guard before #33161, the 4
Self, subclass and protocol subtests pass and the 2 typealias subtests
fail: that guard missed a typealias too.

* fix(defaults guard): Self., typealias, subclass and protocol helper calls reach a Tunable helper

The review of #33161 found Tunable forms the exact helper match missed.
Self.points(...) was skipped because Self is no declared type, and the
guard before #33161 caught it only by the shared-name match. Now:
- Self.helper resolves like a bare helper: the caller's own types.
- A caller's types also include the superclasses and protocols its types
  name, so a subclass reaches its base's helper and a conforming type a
  protocol extension's helper. Only the caller side takes inheritance, so a
  helper file that conforms to Sendable does not reach every Sendable type.
- A typealias of a Tunable, or an alias of such an alias, counts as a
  Tunable token (TD.number, `: TD = .number`). The guard before #33161
  missed this too.
- `class func` / `class var` no longer read as types named func / var.

feat-cmux-next tree: still 449 fingerprints, the same set, all of them in
the old 6182. #32983 and #32979 still report no tunable-source change.
python3 tests/test_defaults_guard.py: 6 subtests red on the test commit,
73 tests OK here. Not resolved: a helper reached through a generic
parameter (T.points).

---------

Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>
lawrencecchen added a commit that referenced this pull request Nov 3, 2026
…een capture; Computer Use refuses it (#33066)

* test(cookie-import): only the person may answer the cookie import consent panel

Red on base: the panel shows in screen capture (sharingType is readOnly), an
Accessibility press or programmatic click on Allow allows the import, and
cmux's Computer Use acts on a window that keeps itself out of screen capture
when its app is one the policy allows.

Review of #32983 (issuecomment-6595849975).

* fix(cookie-import): only the person answers the cookie import consent panel

The Allow/Deny panel before `cmux browser import-cookies` reads another
browser was a custom panel that screen capture showed and that any
automation could answer: an Accessibility press, a programmatic click or a
click another process posts (cmux's own Computer Use, any computer-use
tool, a script) allowed the import. A prompt-injected agent could ask and
then click Allow itself. Now, as macOS's own privacy prompts and Chrome's
permission prompts do:

- The panel is out of screen capture and screen sharing
  (sharingType = .none): an agent that reads the screen never sees Allow.
- Allow answers only the person's own click, read from the click's events
  in the panel's dispatch with the app's strict HID-system check
  (PersonInputCheck, as the join consent sheet): no Accessibility press,
  programmatic click, key, posted click or DEBUG automation allows.
- A click that begins less than a double-click interval after the panel
  shows or moves, or after another click, is not taken (Chrome's
  InputEventActivationProtector).
- Its buttons are the standard CmuxDialogButtonView: Deny with Escape,
  Show Agent Chat, and Allow with the default look and no key.
- cmux's Computer Use helper refuses any target window that keeps itself
  out of screen capture (kCGWindowSharingState none), whatever app shows it.

Deny, Escape, the window's close and the time limit still deny, by anyone.

The join consent sheet's person-click logic moves into CmuxNextDesign as
CmuxDialogPersonClick, so both prompts share one checked implementation
(the join sheet keeps its behavior: no activation delay).

Review of #32983 (issuecomment-6595849975).

* fix(cookie-import): review fixes for the person-only consent panel

- The activation guard arms again on every move of the parent window
  (AppKit moves the child panel with it, so the frame check missed it) and
  on every frame change of the panel itself (an Accessibility client can
  set its position).
- Only the person's own mouse-downs count for the fast-click guard, so
  another process's posted clicks cannot hold the person's Allow off.
- A run with the user origin (the palette, an Import card) skips the panel
  only when the input that started it is the person's own HID input; a
  click or key that a computer-use tool posts there now gets the panel.
- Comments: the Computer Use guard checks only calls that name the window;
  the join consent sheet is not capture-excluded.

* fix(cookie-import): arm the click guard by the panel frame; a person's run needs recent person input

Second review of #33066:
- No setFrame/setFrameOrigin overrides (they need the ObjC selector probe).
  Instead a mouse-down on a panel whose frame changed since it was armed
  arms it again at that click, so that click is not taken; show and every
  parent move arm it too.
- A user-origin run skips the panel only when the event in dispatch is the
  person's own key or mouse-button event from the last second. An
  Accessibility press makes no event, so the event then is an older one
  (a mouse move, an earlier click), which does not count.
- needsConsent has no default for byPerson (a new caller fails closed).
- Tests: a click right after the panel moves is not taken; the recent
  person input rule.

* fix(cookie-import): the person-click dispatch body is main-actor isolated

The window's own dispatch runs on the main actor; saying so lets a caller's
body closure touch main-actor state (Swift 6 refused the move test's capture:
sending 'current' risks causing data races).

* test(cookie-import): the move test follows the person-click test's shape (Swift 6 isolation)

Reverts the main-actor body of the person-click dispatch (it broke the join
consent tests' compile) and builds the move test's click the way the
person-click test does, which compiles.

* fix(cookie-import): a person's run skips the panel only inside the person's own input dispatch

Third review of #33066:
- The person's-run check reads the input event being dispatched now
  (InputDispatchContext), not NSApp.currentEvent, which keeps the last
  event after its dispatch: an Accessibility press within a second of the
  person's click no longer finds that click.
- needsConsent uses ActionInvocation.isPersonsOwnRun: a cmux app's run
  during the person's gesture in it, or a socket run that claims the user
  origin, now asks first.
- An Import card's cookie run keeps the caller's app and socket marks.
- Tests: an app caller and a socket caller ask first.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 8af92c61 Deployed Nov 3, 2026 by austinywang via cmux-tui tree preflight #103867
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-land Reviewed at this head; next-autoland merges it into feat-cmux-next when its only reds are base reds risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants