Repository navigation
cmux NEXT: Allow once / Deny on agent chat approval banners (cx-kz6m0.2) - #20972
Conversation
Red: the banner posts without the approval category and its buttons answer nothing yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…banner test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An agent chat on this Mac that waits on one tool permission now gets a banner that says what it asks to do (tool title and command, scrubbed), with Allow once and Deny (destructive). Each button re-reads the prompt over a person-proven acpmux connection and answers it with _acpmux/permission_respond only while the same request is pending; the decision-to-option mapping is shared with the feed bridge. A prompt that ended or changed opens the tab instead. debug.notifications gains allow/deny actions and an approval field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot needs on-demand usage enabledBugbot 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. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
all web/size checks green |
|
Not verified: macOS jobs did not run: compile and app tests were skipped on CI passes on Written by |
|
Found 1 test failure on Blacksmith runners: Failure
|
Red: a banner whose prompt read outlived an acknowledge or a newer ask still posts, and an older approval banner still answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uttons A banner whose prompt read outlived an acknowledge or a newer notification of its tab no longer posts; a newer banner on a tab retires the older approval banner's buttons, so the map holds one entry per tab. The approval category hides the command where macOS hides previews. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CmuxNextApp is size-capped (SW14). The answerer, the person-proven acpmux line, the banner bookkeeping and category, the prompt helpers the feed bridge shares (digest, frame, shown command, option mapping), and FeedSecretScrubber move to CmuxNextAgentPane, which already holds the acpmux connection types. CmuxNextApp keeps only the wiring and ends 57 lines smaller than its base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…public namespaces package-conventions-lint refuses all-static public enums. The prompt helpers and FeedSecretScrubber stay internal to CmuxNextAgentPane; the app reaches them through extensions on the request dictionary, offered options, String and Data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tions Keeps #20960's finished-banner bursts beside the approval wiring: an acknowledge reads both, and approvals (warnings) never join a burst. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Current head is green on the portable/static checks, but the target branch skipped the macOS compile and app/UI checks. I’m keeping this open until an exact-head macOS build exercises the approval banner in a narrow pane, including stale-prompt retirement, Allow once, Deny, reconnect, and focus restoration. |
|
cmux-next tour of The tour job failed before the tour ran (build or setup); the run log has the output. The tour produced no media. Tours are data in |
… hq-1d) (#20933) * test(agent pane): a chat's tools share the tab row behind chrome.tabs.rowTools With the row offered (tabRow {height, trailingInset}), the chat header is the row's height, ends its tools at the inset and reports their width with pane.tabRowTools; without an offer the header is its own row and reports nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(pane): chat tools in the tab row, a Debug toggle (no top bar) Leo 2026-10-14, "why is there even a top bar". With the Debug setting chrome.tabs.rowTools on (off by default), a chat pane's tab strip gives up the width of the chat header's tools at its trailing end, left of the pane's "...", and the chat page covers the strip's row: the strip and "..." stay above it, and the page draws only its tools there, at the row's height, with a short fade under the row. Off, nothing changes. - CmuxNextDesign: PaneTabRowSharing, ChromeTunables.tabRowTools. - PaneContentView+TabRow: the narrower strip frame, the row offered to the content, the agent name tag kept below the row. - Agent pane: pane.tabRowTools {width} (decoded 0...2000, in AgentPageOps), the tabRow event (replayed on the page host, offered again after a page load), AgentPaneTabRow on the model. The side chat helper moves to AgentPaneSideChat to keep AgentPaneModel under 1000 lines. - Page: header/tabRow.ts; the header's data-tab-row mode; the summary card under the row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: import the tab row's modules publicly for AgentPaneView's conformance Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 6514bc7 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38698999038 * fix: import AppKit publicly for the tab row conformance's CGFloat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: track the tab row tools without state, so a relaunched chat keeps its draft useTabRowTools kept the tools element in state; setting it on mount re-rendered the pane while its first snapshot loaded, and a relaunched new chat lost its kept draft (agent-pane-unsent-draft, red on fad20e4, green here). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: keep the tab row's app code under the CmuxNextApp size cap The zoom and setting checks move to AgentPaneTabRow; AgentPaneView's conformance only forwards to it. The pane sets the content's tools callback with each offer instead of following and unfollowing views, and the agent tag's offset is inline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: move the tab row's pane logic to CmuxNextDesign Base grew to 3 lines under the CmuxNextApp cap. The strip's share of the row and the offer move to a PaneTabRowSharing extension, AgentPaneView's witnesses to a constrained extension in CmuxNextAgentPane, and the App keeps three layout lines and the conformance on its existing Parking line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a re-offered tab row gets the tools' width reported again Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: report the tab row tools' width for every offered row A page that said ready again without reloading lost its width in the App, and the re-offered identical row was deduped, so the tabs ran under the tools. Also keep the agent tag's offset unchanged with the setting off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux + cmux-coderouter: the local router on Windows, owned by a kill-on-close job (cx-ncc.54) The router's owner is one RouterOwner trait (daemon/router_owner.rs) with one impl per platform, so daemon.rs only calls ensure_router at start and release_router at shutdown: - Unix (router_owner/fifo.rs): the cx-1rvy owner FIFO, moved unchanged out of daemon.rs (daemon.rs 1172 -> 949 lines). - Windows (router_owner/job_object.rs): `router serve` starts suspended through CreateProcessW with a handle list (daemon/windows.rs spawn_this, shared with the daemon start) and resumes in a new kill-on-close job (crate::job::Job) the daemon holds, so Windows ends the router when the daemon ends, also on taskkill /F or a crash. A router that already answers belongs to no live daemon and is told to stop. Shutdown asks the router to stop, then closes the job. cmux-coderouter: serve_with_admin(home, bind_admin) runs the router with an admin listener its host binds (after router.lock is held and a stale socket removed); admin_connection reads and writes any async halves. Unix `serve` binds its unix socket (0600) through it. The free tier's attest_provider peer check (18bae8e, cx-ds09i) needs the whole socket, which the split halves no longer give: each accepted admin connection now carries a free_tier::ProviderPeer read before the split (unix: peer_cred uid and, on macOS, the audit token; cheap). provider_peer_allowed takes it and runs the same checks (same user; on a Team-signed macOS build the signed containing app) only when the connection asks to be the provider. Only ProviderPeer::of_unix can pass; the Windows admin socket passes ProviderPeer::refused, so Windows refuses attest_provider (providers are mac and ios only). Unit tests: a refused peer never passes; a same-user socket pair read at accept passes (Linux). The owner FIFO watch stays Unix; on Windows `adopt` answers adopted false (no owner FIFO). acpmux on Windows: `router serve` makes the router folder owner-only and binds the admin socket as a cmux::local_socket listener with the daemon's peer checks (server/windows_socket.rs router_admin); router_socket.rs connects with connect_same_user. Behavior tests (tests/windows_daemon.rs): `acpmux router serve` answers status (build, pid, port) in an owner-only folder and exits 0 after shutdown; a daemon started with ACPMUX_LOCAL_ROUTER=1 starts the router with its build, and taskkill /F of the daemon's exact pid ends the router within 1 s. A router that already answers at Windows daemon start: the macOS rule (cx-1rvy, kept after 03725dc) is confirmed, so the router is STOPPED, not adopted. macOS adopts only a router that answers `adopted`, which needs a live owner (a kept agent host holding the owner FIFO); a router with no owner (no FIFO, or `stopping`) and no kept agent is told to `shutdown` and a new one starts (it waits on router.lock). On Windows only the daemon owns the router (its kill-on-close job) and daemon.lock allows one daemon, so a router that answers at start has no live owner: same rule, stop it. 03725dc itself changes only the app's lease (it sets the upstream again when the relay pid changes), so a replaced router gets its bearer again. Release ignores `stop` on Windows (no kept agent hosts): accepted for now, follow-up cx-xs01y (a shared job object if agent hosts come to Windows). * acpmux: the Windows router admin socket binds again for up to 1 s (cx-ncc.54) Review finding: a router of another build that just released router.lock may still hold the old socket file for a moment; the bind then failed with no output (the router's output goes to NUL). Remove and bind again, bounded at 1 s. * acpmux: router_owner blank lines as rustfmt wants them (cx-ncc.54) Base reds: exact base 062b82b (landing 6 head, cmux-tui full run 38530387510). The gate excludes a red job only when that run has the same job red with the same error lines; expected: "valgrind-leak-check (startup)" and "valgrind-leak-check" (red on feat-cmux-next 12b1e29, run 38357900702; valgrind 0 errors). This landing changes only cmux-tui/crates/acpmux and cmux-tui/crates/cmux-coderouter. Windows VM proof (gpuitest, router e5cc1d8, same acpmux and cmux-coderouter code apart from tip changes in hub/ and tests): windows_daemon 11 passed, 0 failed, incl. a_killed_daemon_takes_its_router_with_it. * acpmux: the Windows start tests daemon.lock with a read, not a lock (cx-ncc.54) wait_for_winner (daemon/windows.rs) waits while another start's daemon holds daemon.lock and has not bound its socket yet. It tested the lock with File::try_lock, which took the lock for an instant when it was free: a third start's daemon that tried the lock in that instant exited too. Now a 1-byte read at offset 0 on a read-only handle tests the lock and never takes it. ERROR_LOCK_VIOLATION (33) means another handle holds the lock (LockFileEx over the whole range, as File::try_lock takes it); a read or ERROR_HANDLE_EOF (38, std returns it as Ok(0)) means the lock is free. Proven on the Windows VM (Windows Server 2022, NTFS, as gpuitest, folder C:\build-wb\acpwin-probe, /tmp/acpwin/l4/probe/lock-probe.ps1): with a holder in another process, an empty and a 1-byte daemon.lock both gave 33; free, both gave 38. The outdated RISK comment is replaced. * acpmux + cmux-coderouter: router review fixes (hq-84 P3, cx-ncc.54) hq-84 approved the per-connection peer decision (c12e44a) with two P3 follow-ups, both done here: - free_tier::ProviderPeer is neither Clone nor Copy: a peer read for one connection cannot be handed to another (serve_with_admin is public). - The Windows router admin bind retry (server/windows_socket.rs) removes the old socket file only after the same check bind_unix makes: another user's socket is refused and never removed, a socket that still answers (a stopping router) is left until it goes, a stale file of our user is removed. Also admin_connection takes the AdminConnection as one value, so the clippy::too_many_arguments allow is gone. * ci: cmux-tui-rust-check tests run under cargo-nextest; a failed test reruns once alone and passes as FLAKY (named), or stays red cargo test ran the 361 test binaries one after another: 765 s of tests on rbx, 998 s on cmux9. nextest runs them at once: 55-60 s on rbx at 32 threads. The full run's reds were load reds (a different set every run, each green alone; cx-mqvyw, cx-t9m9q). A lower thread cap did not remove them (16 threads: 1 flaky in 2 runs, 99-109 s; 8 threads: 190 s). So a failed test runs once more alone after the full run (2 s on rbx): a test that fails alone stays red; one that passes alone is printed as gate-notice: FLAKY with its name. Doc tests are their own step. The 4 Linux load-red skips go away (the rerun covers them); protocol_ir_is_current is a base red (cx-ngrbq). * ci: cmux-tui-rust-check reports the macOS PTY peak of the full run (cx-leo45) * ci: rust-check review fixes: the PTY sampler has no stdout and dies with the step; nextest extracts beside its final path; rerun_notes * webviews tests: stop the Vite a vp dev server started (#21062) * test: stopping a vp dev server stops the Vite it started Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: browser tests stop the Vite their vp dev server started SIGTERM to vp left its Vite child running; every dev-server test leaked about 1.1 GiB per run, and on react-apps-check the leaks starved the dev server files after a failing menu-focus into timeouts. stopDevServer stops vp's children by parent pid, then vp, and keeps the server in the test's process group so the runner's timeout still reaches it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: the dev server warm check covers tests that load pages dev-server-stop starts and stops vp dev without a browser, so it has nothing to warm. Also read pgrep's output safely when it is missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux NEXT: Allow once / Deny on agent chat approval banners (cx-kz6m0.2) (#20972) * test(cx-kz6m0.2): agent chat approval banners offer Allow once and Deny Red: the banner posts without the approval category and its buttons answer nothing yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cx-kz6m0.2): import NotificationSource's module in the approval banner test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cx-kz6m0.2): Allow once and Deny on agent chat approval banners An agent chat on this Mac that waits on one tool permission now gets a banner that says what it asks to do (tool title and command, scrubbed), with Allow once and Deny (destructive). Each button re-reads the prompt over a person-proven acpmux connection and answers it with _acpmux/permission_respond only while the same request is pending; the decision-to-option mapping is shared with the feed bridge. A prompt that ended or changed opens the tab instead. debug.notifications gains allow/deny actions and an approval field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cx-kz6m0.2): approval banners skip a read or superseded ask Red: a banner whose prompt read outlived an acknowledge or a newer ask still posts, and an older approval banner still answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cx-kz6m0.2): approval banners skip stale reads and retire older buttons A banner whose prompt read outlived an acknowledge or a newer notification of its tab no longer posts; a newer banner on a tab retires the older approval banner's buttons, so the map holds one entry per tab. The approval category hides the command where macOS hides previews. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(cx-kz6m0.2): approval banners live in CmuxNextAgentPane CmuxNextApp is size-capped (SW14). The answerer, the person-proven acpmux line, the banner bookkeeping and category, the prompt helpers the feed bridge shares (digest, frame, shown command, option mapping), and FeedSecretScrubber move to CmuxNextAgentPane, which already holds the acpmux connection types. CmuxNextApp keeps only the wiring and ends 57 lines smaller than its base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(cx-kz6m0.2): prompt and scrubber helpers as extensions, not public namespaces package-conventions-lint refuses all-static public enums. The prompt helpers and FeedSecretScrubber stay internal to CmuxNextAgentPane; the app reaches them through extensions on the request dictionary, offered options, String and Data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: reasoning menu drawn as a native menu (#20787) * test: reasoning menu rows share one check gutter and Ultracode's note is one short line The reasoning-claude gallery variant now offers Extra High on Opus, so its menu shows Ultracode as its note promises. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: reasoning menu drawn as a native menu Ultracode's two-line grey description becomes a short note beside its name (Extra High + multi-agent, every locale), with the text as its tooltip. The check moves into a fixed leading gutter shared by every row, so labels align whether or not a row is checked, as in the access menu. Rows are one height; section headers sit on the label column in the system's caption style; the separator is inset to the rows with even spacing. The access menu's 1-4 numbers become secondary key-equivalent text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * CmuxNext: reasoning menu rows and headers use logical padding for RTL The asymmetric row padding and the header's gutter indent were physical (left/right), so in Arabic the check gutter sat on the right while the extra inset stayed on the left. Use padding-inline so both mirror. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: reasoning menu rows with Ultracode as its own On/Off section The base now draws Ultracode as a switch section after the levels; the one-line note sits on its On row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 8902b9d - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38699733289 * CmuxNext: mark Ultracode's note tooltip as allowed (the menu may cut the note) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * cmux NEXT: /btw side sessions share one right pane; pane relay allows _acpmux/btw (#20826) * test: side sessions share one right pane and the palette asks a side question Behavior tests for /btw side sessions: _acpmux/btw goes out through the pane relay like a prompt and its side session opens beside the chat; later side sessions of a chat open as tabs in its one right pane; Ask a Side Question… is a palette action that sends the page sideQuestion. The tracker and view command are stubs here (old behavior), so these fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cmux-next): /btw side sessions share one right pane; Ask a Side Question… chat.sideChat (New side chat and /btw side sessions) opens a chat's later side sessions as focused tabs in the pane its first one split off to the right, instead of a new split each time (AgentSidePanes, one tracker per chat session, opens one at a time like AgentSubagentPanes). The pane relay allows _acpmux/btw (sessionId, question, model): session scoped, gesture required like a prompt, and its reply's sessionId joins the pane's scope so chat.sideChat may open it. Ask a Side Question… (agentPane.sideQuestion) is a palette action for a focused agent chat, with no default key; it sends the page sideQuestion, which the page ignores while its side sessions gate is off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(cmux-next): regenerate action exports for Ask a Side Question… From scripts/measure/export-action-surfaces.sh (fleet light step) on 004a08b. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: the pane relay refuses the inline side question _acpmux/ask /btw now starts a side session (_acpmux/btw); the page no longer sends _acpmux/ask and the Rust policy drops it (web PR). The relay still passes it here, so this fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next): the pane relay no longer allows _acpmux/ask The page sends _acpmux/btw for /btw now and the Rust pane policy drops _acpmux/ask (web PR), so the Swift allowlist, its session scope, gesture rule and params entry drop it too and the two lists stay mirrored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * revert(cmux-next): drop the Ask a Side Question… palette action for now Review: it shows for every NEXT user and does nothing while the page's side sessions gate is off, the default. It returns with the Labs flag follow-up, gated by requires on labs.sideSessions. Removes its catalog group, handler, strings, AgentPaneView.askSideQuestion (no other caller), its test, and its action exports (back to the base copies; regenerated on the fleet to confirm). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a chat whose side tabs all closed keeps no side-pane tracker AgentSidePanes keeps one tracker per chat session for good today, so this fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next): drop side-pane trackers whose side tabs all closed AgentSidePanes.chat drops idle trackers of other chats with no side tab still open (read through the latest open's pane lookup), so the registry does not keep one per chat that ever had a side session. The test layout's pane lookup holds it weakly, as the app's does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(cmux-next): side-pane placement lives in CmuxNextAgentPane, not the App The App size cap fails when a change adds lines to CmuxNextApp. The side pane's single placement closure (AgentSidePanes.open(_:chat:pane:place:)) moves into CmuxNextAgentPane, and the App reuses openBeside, which now focuses a side session it adds as a tab, so CmuxNextApp does not grow. AcpmuxPaneMethods.swift stays at its 400-line budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cmux-next): /btw and Ask a Side Question… follow Debug Settings' Side sessions Red first: with agent.sideSessions off (the default) the palette has no Ask a Side Question… and the page's header state says side sessions are off; with it on the action shows in a focused agent chat, the page is told, turning it off hides the action again without reopening, and running the action sends the page the sideQuestion command (prefill). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cmux-next): Side sessions (/btw) Debug Settings toggle gates /btw and Ask a Side Question… agent.sideSessions (Debug Settings > Pages, "Side sessions (/btw)", off by default, DEV and NIGHTLY) replaces the page's side-question experiment. The page reads it as sideSessions in the chat header state (pane.tabState and the headerState push); the header watch observes the toggle, so a change reaches open chats without reopening them. Ask a Side Question… returns (from 161e75f) in the palette only, no key, requiring the new sideSessions context fact, which ActionRegistry.publishSideSessions() keeps in step with the toggle, so it is hidden while the toggle is off. Its handler shares one loop with Switch Model… and Search Files…, so CmuxNextApp does not grow; the AgentPaneView and AgentPaneModel god types do not grow either. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(cmux-next): regenerate action exports for Ask a Side Question… From export-action-surfaces.sh on fleet step 82e608fc0e5b3b3b3c3708a7 (light, aws-m4pro-9) at c8dbb22. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(cmux-next): regenerate action exports on the merged head The textual merge of action-surfaces.json went stale; regenerated by export-action-surfaces.sh on fleet step 3384e925ec431e91b8a90d09 at 986fed6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next): name the sideSessions context fact for the control bridge RegistryReachabilityTests runs every action with every named context fact; the bridge's own name table lacked sideSessions, so Ask a Side Question… stayed unavailable there (fleet suites at 47266ba). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(cmux-next): ActionCatalog.xcstrings is base's plus Ask a Side Question… The textual merge rewrote 27 of base's lines; this is base's catalog with only action.agentPane.sideQuestion added (no key or value lost). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(cmux-next): regenerate action exports on the merged head export-action-surfaces.sh on fleet step 715bded4f5ecea4562b53200 at 000d2fd; the textual merge of the exports went stale again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at c6ec002 - regenerated plans/cmux-next/action-surfaces.json - regenerated plans/cmux-next/actions.md Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38724924351 * next: move Ask a Side Question… to a follow-up; keep the Side sessions toggle The palette action, its context bit, catalog strings and action exports leave this PR (plans/cmux-next exports are frozen). The Debug Settings toggle and the header-state plumbing that gates /btw stay, with a header test for the off and on states. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> Co-authored-by: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Co-authored-by: safe-push <safe-push@cmux.local>
…es its lines #20972 (d5292fb) started a main-actor Task per acpmux line and decoded every reply on the main actor (main-actor work ratchet, baseline 0). Each line now decodes on the line's queue into an id and its result, and the replies reach the main actor through MainActorLineBatch (at most one drain pending; the close after the last line). MainActorLineBatch moves from CmuxNextAgentActivity to CmuxNextCompat so CmuxNextAgentPane can use it without a new package dependency; the AgentActivityLineConnection helper stays in CmuxNextAgentActivity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An agent chat that waits on a tool permission now gets a banner with Allow once and Deny (destructive), as classic's feed permission banners have. The banner body says what the agent asks to do (the tool call's title and command, secrets scrubbed) instead of the chat's last message. Parity gap row 2 of cmuxterm-hq#2099, bead cx-kz6m0.2.
How it answers:
_acpmux/info) on a short connection proven as the person (the sameinitialize+_acpmux/person_provehandshake the feed bridge uses). The category is attached only when exactly one tool permission is pending and it offersallow_onceand a reject option. Questions, several prompts, terminal tabs, and chats on another Mac keep the plain banner..authenticationRequired). Each button re-reads the prompt and answers with_acpmux/permission_respondonly while the same request (sha256, as the feed bridge checks) is pending. The decision-to-option mapping is now one function shared withAcpmuxPermissionFeedBridge. A prompt that ended or changed answers nothing and opens the tab.debug.notificationsgainsallow/denyactions and anapprovalfield per banner, for dogfooding without clicking.Not covered: terminal agents' hook approvals ("Claude needs approval", body "Bash"). NEXT's hooks only notify and nothing blocks on an answer, so there is no prompt to answer from a banner. That needs a blocking hook path in cmux-tui and is left for a follow-up.
Where the code lives: CmuxNextApp is size-capped (code-quality.md SW14), so the answerer, the person-proven acpmux line, the banner bookkeeping and category, and the prompt helpers the feed bridge shares (digest, request frame, shown command, option mapping), plus
FeedSecretScrubber, are inCmuxNextAgentPane/Approvals/. The prompt helpers and the scrubber stay internal there; the feed bridge reaches them through extensions on the request dictionary, the offered options,StringandData(package-conventions-lint refuses all-static public enums). That target already holds the acpmux connection types. CmuxNextApp keeps the wiring inNotificationCenterService+Approvals.swiftandDesktopNotifier, and ends 57 Swift lines smaller than its base. Package.swift is unchanged.Verification
Fleet
scripts/ci/run-swift-testing-suites.sh Packages/macOS/CmuxNext, focused onApprovalBannerTests(boundary test: a daemon notification goes throughNotificationCenterService.deliver, the test readsdesktop.postedand the answers a stand-in answerer got):Suite ApprovalBannerTests failed after 0.013 seconds with 5 issues(no approval category, body is the last message, buttons answer nothing).Suite ApprovalBannerTests passed after 0.011 seconds.Suite ApprovalBannerTests passed after 0.009 seconds, and every suite in the run passed.scripts/cmux-next/check-release-compile.shat 04c964e (step d18f6ef576562df80edd4be2):** BUILD SUCCEEDED **.Review fixups, red then green with
ApprovalBannerTests(three new cases: a read outlived by an acknowledge, a newer ask during a read, an older banner's buttons):Suite ApprovalBannerTests failed after 0.018 seconds with 3 issues.Suite ApprovalBannerTests passed, and every suite in both runs passed.** BUILD SUCCEEDED **.After the move, at b893776 (
check-app-size.sh: ok, 113672 lines, 20 under the cap 113692):Suite ApprovalBannerTests passed. Other failures in those runs: SidebarInsertWhileDisplayAsleepTests (also red before this change), and one CloudBringBackTests or CloudMoveTests case per run, each a different test timing out on the same worker.** BUILD SUCCEEDED **.After the lint fix, at ca386d5 (
check-app-size.sh: ok, 113672 lines;lint-ios-package-conventions.sh: no ERROR lines):Suite ApprovalBannerTests passed, and every suite in the run passed.** BUILD SUCCEEDED **.After merging feat-cmux-next with #20960 (finished-banner bursts), at f99c1fe (
check-app-size.sh .: ok, 113665 lines, 70 under the cap 113735). Approvals are warnings and never join a burst; an acknowledge clears both:Suite ApprovalBannerTests passedandSuite FinishedBannerBurstTests passed. ClassicShortcutsUpgradeTests and SettingsSchemaExportTests failed in that run; this branch touches no settings, shortcut or schema files.** BUILD SUCCEEDED **.Not verified live: no capture of the banner's buttons on a real Mac yet; the acpmux connection path is exercised only by Release compile, not by a running acpmux.
Localization: three new strings (
notifications.approval.allowOnce,notifications.approval.deny,notifications.approval.hiddenBody) with all 21 locales in CmuxNextApp's Localizable.xcstrings.Base reds in
cmux-next checks, not this change and not gating (coordinator): Motion tokens (ColorTransition.swift), Type scale (BrowserFitFrameView.swift), Package conventions lint (BuiltInAdBlockerSetting.swift). Thecompilequeue check is stuck and does not gate; the fleet Release compile above covers it.Changelog
🤖 Generated with Claude Code