Repository navigation
acpmux: one typed protocol registry; generated web types and drift checks (hq#2025, no bead) - #20576
Conversation
One registry names every acpmux method and notification; sessions and permissions carry serde/schemars types, and the daemon builds SessionSummary and PermissionGroup from them. ir() emits spec/acpmux-protocol.json for the web codegen. Tests fail when the committed IR is stale or the dispatcher serves a name the registry lacks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ck (hq#2025, no bead) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ession_changed use them (hq#2025, no bead) The permission group client and permission_respond go through the typed request; session_changed and permission_pending are narrowed by the registry's notification map. The IR declares a peer summary's optional fields and peer key, permission_pending's always-sent keys, and closed param objects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…x-typed-protocol # Conflicts: # webviews/package.json
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
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 |
This comment has been minimized.
This comment has been minimized.
|
all web/size checks green |
|
Passes: CI passes on CI passes on Written by |
|
|
|
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 |
…ide the pane sources Its developer-facing IR errors tripped pane-english, which scans src/agent-session/acpmux for user-visible English. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gallery227 states changed: gallery.visual-media-board/all-previews, agent-pane.edited-files/fourteen-files, agent-pane.transcript/shell-grouped-commands, pages.settings/notifications-group-2, pages.settings/general-group-3, ... · 2 new states · 48 broken states · 106 play checks failed Diff page and renders (the run's 221 more on the diff page. Live preview (follows this branch; start it with
Play checks failed (0 px anchor movement, 0 layout shift, no frame over 33 ms unless the entry gives a reason):
636 nondeterministic states differed from a second render of the same head (a clock or fixture leak) and are not counted as changes. |
- cargo fmt in Native/OptChat/optchat-chief Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38569912312
Base (#20362) adds each session's lane and `inputSince` and derives `indicator` and `lane` with `views::decide`. The typed SessionSummary gains `inputSince`, `lane` ({state, since, error}) and `lastTurn.detail`, and session_summary calls `decide` on the serialized summary. Spec and generated TS are regenerated. 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. |
This comment has been minimized.
This comment has been minimized.
Base serves _acpmux/process/list and process/kill and sends _acpmux/process_update; the completeness test caught them. Spec and generated TS are regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…x-acpmux-typed-protocol
- vp fmt webviews/src/pages/home-channels/cloudHome.ts - cargo fmt in Native/OptChat/optchat-chief Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38649959912
|
Visual preview of 594928e: in the maintainers' private review feed. |
This comment has been minimized.
This comment has been minimized.
Base's protocol registry (#20576) lists every method the daemon serves; this PR's attachment read joins it, untyped like _acpmux/events. Spec and generated web method table updated to match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in the protocol registry check:browser-lanes requires warmViteServer once a test's Vite listens. Base's protocol registry (#20576) must name every rpc::method constant: _acpmux/start_mode and _acpmux/start_mode_changed join it, the IR, and the generated web method list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#20650) * test: a pending new chat's mode chip names its start mode (cx-wx93i) Fails on base: after Normal is picked in one pane, a new chat in another pane that acpmux has not started yet shows Full access. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: a pending new chat's mode chip names the mode acpmux starts it in (cx-wx93i) A chat acpmux has not started yet drew its access chip from the mode the harness's last attached chat reported (any pane), or the label the last chat showed. acpmux starts a new chat in the level of the person's latest pick (hub/start_mode.rs), so the chip said Full access for a chat that then started asking. The harness profiles now keep the mode a chat acpmux just started reported (startMode) and the level of the latest mode pick a local pane made; a pending chat shows the pick's mode when newer, mapped onto the harness's own ids as acpmux maps it, else the last start mode, else no mode at all. Profiles re-read storage another pane changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: name Bun's socket type without importing bun's globals Importing a type from "bun" pulls Bun's ImportMeta.hot into every source file, which broke the dev-host HMR callbacks' types. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: acpmux answers the mode a new chat starts in (cx-wx93i) _acpmux/start_mode names the mode a plain new chat on a harness starts in, and a session/new then starts there: full access from config, then Normal after a pick in a chat. Picks in a preset's chat or a Web chat change nothing, and a Web connection gets no prediction for its own chats. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: acpmux answers the mode a new chat starts in (_acpmux/start_mode, cx-wx93i) The pane mirrored acpmux's start-mode rules and could not see what they depend on: preset, folder-profile and remote-origin chats, profiles that pin a mode in argv, the defaultPermissionMode setting and webAskingModes. acpmux now answers it: `_acpmux/start_mode {harness?, preset?, cwd?}` returns `{harness, modeId, name, level, source}` for a plain new chat, or modeId null when session/new would set no mode. One rule set: session/new and the answer both decide with `start_mode::mode_eligible` (lifecycle.rs used to inline it), `start_level` (latest pick, else config.json, else the setting) and `mode_for`. The harness's modes come from its newest chat in this daemon, else acpmux's own Claude backend list; unknown modes answer null. Read only: no config write, no spawn. Origin: a connection gets the answer for its own context, as `_acpmux/prewarm` and session/new take `remote` from the connection. A Web or Peer connection's session/new sets no start mode, so it is answered modeId null rather than refused; the answer reveals only the level and mode id, which the same caller's next chat would show anyway. Answered locally, never forwarded to a peer. No person or approval gate changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a pending chat's mode chip names acpmux's start-mode answer (cx-wx93i) The stand-in acpmux answers `_acpmux/start_mode`: the latest pick's mode, or null for a profile that pins its mode. After Normal is picked in pane A, pane B's new chat shows Normal; when acpmux would set no mode, the chip names none ("Mode"), even after a pick. Red: on the merge-base the first case shows "Full access" (the last chat's mode); on the pane-side mirror the second shows "Normal" (it guessed a pick acpmux does not take). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: a pending chat's mode chip names acpmux's start-mode answer (cx-wx93i) The pane no longer mirrors acpmux's start-mode rules. When a pending new chat is created, the pane asks acpmux `_acpmux/start_mode` for its harness and folder and names that mode; until the answer comes, or when acpmux sets none or predates the method, the chip names no mode ("Mode"). - harnessSwitch: the switch asks through its port before session/new and sets the pending chat's currentModeId from the answer; a pick made during the switch still wins, and the session's own report replaces it. - ComposerPickers: the New Tab PendingChip asks the same (useStartMode, startMode.ts) when no modes are known. The access menu's value no longer falls back to the first mode, so the first mode can be picked while the trigger shows "Mode". - Deleted: the pick recording in App.tsx, harnessProfiles' pick storage, level tables, startMode/startedAt/family fields and its per-read localStorage reload (harnessProfiles.ts is as on the base again). - The pane relay allows `_acpmux/start_mode {harness, cwd}` (policy.json, AcpmuxPaneMethods.swift): read only, no gesture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at b0b3782 - vp fmt webviews/src/pages/home-channels/AttachmentViews.tsx - vp fmt webviews/src/pages/home-channels/Composer.tsx - vp fmt webviews/src/pages/home-channels/HomeChannelsPage.tsx - vp fmt webviews/src/pages/home-channels/attachmentService.ts - vp fmt webviews/src/pages/home-channels/icons.tsx Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38580001182 * test: the remote-origin start-mode case runs a Web Codex chat A remote chain runs Claude Code only inside the macOS sandbox, so on Linux the Web Claude chat was refused before the case reached its point. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at fbeb5e6 - regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38600637207 * test: start-mode answer notices, remote callers and harness kinds (cx-wx93i) - A pick acpmux saves tells local watchers `_acpmux/start_mode_changed`; a Web watcher is not told, and Plan is no pick. - A Web connection asking with a cwd under a folder profile gets the plain null answer, never an error naming the folder. - A folder profile, an argv-pinned profile and Codex before any chat of it answer as session/new would start them; the settings file's agents.defaultPermissionMode answers with source "settings". - Pane relay cases: every known param of _acpmux/start_mode, preset and _meta refused, no forward mark, cwd through the path check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: acpmux tells local watchers when the start mode may change (cx-wx93i) - `_acpmux/start_mode_changed {}` goes to watching local connections (never Web or Peer) when note_mode_pick saves a new level and when a config reload changes defaultPermissionMode or lastPermissionMode; a pending chat's pane asks `_acpmux/start_mode` again. - `_acpmux/start_mode` answers a Web or Peer connection with the all-null answer before resolving its cwd, preset or harness: a folder-profile refusal could otherwise name folders outside the Web roots. - It waits for startup like session/new (harness discovery and launcher checks) before answering. The cmux settings file (agents.defaultPermissionMode) is not watched: a settings write sends no notice. The pane also asks again when it becomes visible or focused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a pending chat follows later picks and draws a kept answer first (cx-wx93i) - Browser: the pending chat opens first in full access, then Normal is picked in another pane; the stand-in acpmux sends `_acpmux/start_mode_changed` and the pending chat must name Normal before any send. - jsdom (#20579): the first frame draws acpmux's kept answer with its warning treatment; a wrong kept answer gives way to acpmux's answer; no answer names no mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: a pending chat's mode chip follows acpmux's answer as it changes (cx-wx93i) - startMode.ts keeps acpmux's last start-mode answer per harness and folder (never a chat's mode). A pending chat draws it on the first frame, asks again on mount, and the answer replaces it (cx-uq9xi's warning treatment from the first frame, now from acpmux's answer). - Pending chats ask again on `_acpmux/start_mode_changed`, after this pane's own successful mode pick (its kept answers follow the pick at once, as older daemons send no notice), and when the pane is shown or focused again (a settings change sends no notice). - The harness switch draws the kept answer and redraws when it changes; the switch port's one-shot startMode is gone. - A chat on another machine (peer, cloud, remote connection) draws no local answer: "Mode" until its session reports. - ComposerPickers' chip comment names startMode.ts for the access chip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: the start-mode notice rides the watchers' harness feed hub/mod.rs and server/mod.rs are over the god-file limit on the base; the notice's own broadcast and fan-out arm grew both. The notice now goes out on the existing harness feed, marked `startModeChanged`, and server/harness_admin.rs `watch_note` turns it into `_acpmux/start_mode_changed {}` for local connections only (Web and Peer get nothing); every other payload is `_acpmux/harnesses_changed` as before. Neither file grows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: check.json holds 44 cases with start_mode's path check Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at bca4ffe - regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38643004564 * fix(next): new chat's access chip paints on the first frame from acpmux's kept start mode The start-mode answer cache was keyed per harness and folder, so a folder acpmux had not answered for yet drew no access chip until the answer came, and the chip then pushed in. A folder with no answer of its own now draws the harness's latest kept answer (answers are kept latest last); acpmux's answer for that folder then replaces it. An answer of "none" is kept as such, so a folder where acpmux sets no mode never borrows another folder's. Tests: composer-location-strip and new-tab-composer-first-frame seeded the access chip from chipMemory's `mode`, the prediction source this PR removed on purpose (the last chat's mode is not the next one's). They now seed acpmux's kept start-mode answer instead (for another folder, so the first-frame fallback is exercised); their no-placeholder, same-chips and no-shift assertions are unchanged. Red (e40e709, CI job 116128466473 and locally): bun test test/new-tab-composer-first-frame.test.tsx -> 1 fail CMUX_BROWSER_TESTS=1 bun test test/composer-location-strip.test.ts -t "effort and access" -> 1 fail Green: both pass; access-chip-pending-warning and agent-pane-start-mode-chip pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a folder acpmux has not answered never borrows another folder's mode Red on 4ae6985: the pending access chip names another folder's Bypass permissions (with its warning) on the first frame, and applySwitch sets it as the switching chat's currentModeId. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: another folder's start mode only holds the access chip's place cachedStartMode marks an answer borrowed from another folder (acpmux has not answered for this one yet). The pending access chip draws it as the neutral "Mode" label (existing picker.mode string, no new strings), with no unrestricted warning, so it never names a mode a folder profile or preset may not start the chat in. applySwitch and askStartMode use only this folder's own answer. new-tab-composer-first-frame: the borrowed first frame now labels the access chip "Mode" instead of the other folder's "Ask before edits"; the chips, their order and the settled chips after the report are asserted as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: warm the start-mode chip test's dev server; declare start_mode in the protocol registry check:browser-lanes requires warmViteServer once a test's Vite listens. Base's protocol registry (#20576) must name every rpc::method constant: _acpmux/start_mode and _acpmux/start_mode_changed join it, the IR, and the generated web method list. 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>
* feat(cx-gakp): unify slash commands, skills and prompts Keep the existing Milkdown editor and its URL, mention and attachment behavior. Label ACP command sources so skills and prompts share the same searchable picker. * chore(next): PR autofix at cade19a - vp fmt webviews/src/agent-session/acpmux/composer.gallery.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38535762014 * fix(cx-gakp): localize slash picker labels and cmux command descriptions The Skill/Prompt labels and the four cmux command descriptions were hard-coded English, which pane-english rejects. They now come from the pane's table in all 21 languages; cmux commands carry a descriptionKey. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cx-gakp): keep Codex skills' $ prefix in the slash picker codex-acp names each skill `$name` and passes `/$name` through to Codex as a skill mention. Stripping the $ inserted `/name`, which ran a same-named builtin or nothing, and deduped the skill behind cmux's own commands. Keep the name and use the $ as the skill signal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at f4ed982 - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38541336447 * fix(cx-gakp): read the skill tag from ACP _meta in the slash picker acpmux will tag Claude skills as _meta.kind (ACP keeps extensions in _meta); read it alongside codex-acp's $ prefix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cx-gakp): slash picker play finds its rows by name; harness stays harness - The gallery play waited for exactly 3 rows, but cmux's /import shares the menu; it now finds the fixture rows by name, asserts Skill and Prompt on their own rows and no label on a harness command, and clears the "/" before typing the draft. - The comment no longer claims acpmux tags Claude skills. - /continue's description keeps "harness" as the catalog does elsewhere (17 locales said "tool"). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate pane strings after the base merge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: regenerate pane strings after the base merge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Chief registry: chief.profile.adopt makes an existing agent participant a Chief record under its own id The owner (a trusted local Unix caller acting as the person) adopts an agent that is already in a conversation: the record keeps the participant's id and display name, so its DMs, history and credential stay its own, and gets chief.profile.create's defaults and only the fields given. A page (WebSocket or the host's page relay, even with a gesture), a paired or remote device and a bound agent get origin.forbidden; there is no page route. agent_mux is default_chief, a live record already_chief, a deleted Chief deleted_chief (an adopt never revives its tombstone or kept memory), and a person or an unknown id not_an_agent_participant. The write goes through the daemon's registry ledger (idempotent per key) and the same chief_written hook as a create. Capability chief-profile-adopt-v1; CLI cmux chief adopt AGENT [PROFILE], refused for an agent caller. Real-daemon tests for each refusal and for an adopt that keeps the DM and its history. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chief.profile.adopt review fixes: owner gate first, any page origin refused, brain directory collisions refused, tests for the event and the limit Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chief.profile.adopt: refuse a bound participant, revoke its earlier credentials in the same write, and only the brain (or, with no supervisor, the person's terminal) mints the Chief's credential The adopt claims the participant inside the registry write: under the bindings lock it refuses a participant with a bound connection (participant_bound, "stop its current agent session first") and otherwise revokes every credential issued for it. conversation-bind now checks the credential and binds under the same lock, so a revoked credential never binds after the adopt. For a Chief record other than agent_mux, conversation-agent-token is refused while a brain supervisor runs (the supervisor mints the brain's credential in process); with no supervisor only the person's own terminal mints it, never a page, a paired or remote device or an agent. Tests: a bound participant is refused; after it unbinds the adopt succeeds and the brain starts; the old credential is refused; the brain's credential binds; the person mints the post-adopt credential on a daemon with no supervisor; a page, a device and an agent cannot mint it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chief.profile.adopt: the Chief-credential check and the mint run under the registry lock Review finding: a mint that passed the Chief check before an adopt could overwrite the brain's credential after it. conversation-agent-token now checks and mints inside one registry-locked step (registry, then the conversation store, the adopt's order). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Regenerate bindings after merge Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * chief.profile.adopt: clippy (one write struct for mutate, no redundant clone) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * resource_attach: chief.profile.adopt is the 54th connection operation Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-next: CmuxNextApp cap back to 113692 (frame-strip proof analysis and animation palette toggles leave the App target) (cx-me3xd) The debug.new_tab frame_strip analysis (frame comparisons, PNGs, report fields) moves to WindowFrameStrip in CmuxNextDesign beside WindowFrame and MotionFrameProgress; the drag_new gesture moves to TabStripDebug.dragFirstTab in CmuxNextTabs. DebugFrameStrip keeps only the wiring (record, send key or gesture, spans), so plus, drag_new and every report field stay. The ui.animations.<kind> toggles and bounce bind through AnimationKindsSetting.bindActions (CmuxNextSettings) on the shared SettingChoiceActions save, as ChromeStyleSetting does (ported from nx-grow-land-fallback b1e0c989). CmuxNextApp 113810 -> 113692; the cap follows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cx-tgf4h red: cmux chief -p under ACPMUX_AGENT_LAUNCH=deny fails at once with a typed refusal * cx-tgf4h: the Chief says at once that agents are disabled in this build session With ACPMUX_AGENT_LAUNCH=deny (the nx-remote gui/input default since HQ #2130) or CMUX_AGENTS_DISABLED=1, no Chief turn can start an agent. `cmux chief -p` started a daemon and a brain and waited 240 s for a reply that could not come. - cmux-tui-core agent_launch: one test of the kill switch (acpmux launch_policy.rs), the reason agent_launch_disabled. - `cmux chief` (pipe and chat) refuses before it starts or sends anything: operation.failed, details.reason agent_launch_disabled, exit 1. - The daemon's supervisor starts no Chief brain; chief.engine.* and chief.stop answer the typed refusal at once. - optchat-chief host does not start (logs why, exit 0). - Home: the local Chief conversation's notice says agents are disabled; the engine panel maps the typed refusal (localized, all Home languages). - Every Chief proof script refuses at once under deny and names `nx-remote --allow-agents`. * Channels view: gallery plays for the host's home.currentSelection (red: the page does not serve it yet) The host-callable command type (hostActions.ts) and two plays: a conversation, the keyboard's current message and the open thread; the docked list pane answers null. cx-hnmbt Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view: home.currentSelection for the Home dock's view switch The page serves window.cmuxHomeActions["home.currentSelection"] (the agent page's cmuxAcpmuxActions pattern; the host calls it with callAsyncJavaScript): {conversation, message?, thread?} = the open conversation, the keyboard's current message or the message a link marked, and the open thread's root; null on the narrow list pane or with nothing open. The host never reads the page hash for the hand-off. cx-hnmbt Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view: serve home.currentSelection on the page bridge, not a window global The host calls it with PageRouter.callPage (the typed host-to-page call the page bridge already has; PageClient.handle on the page): {} -> {conversation, message?} | null. message = the keyboard's current message, else the message a link marked, else the open thread's root. The gallery plays drive a real BridgePageClient through __cmuxPageReceive, as the app does. cx-hnmbt Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view: home.currentSelection review follow-ups Layout effect for the answered selection; null before the page's first measure (a docked list pane never reports a hidden conversation); the gallery host uses a private bridge target; the thread case is its own play (the gallery opens the thread). cx-hnmbt Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * acpmux: Codex picks run the in-process bridge, not the npx codex-acp adapter (#20800) * test(acpmux): the npx codex-acp adapter is offered beside the Codex bridge Red: with codex and npx on PATH and no codex-acp, discovery keeps the pinned npx adapter (@agentclientprotocol/codex-acp) as `codex-acp` next to the in-process bridge. That adapter reads the account through codex's account/read, which fails for a ChatGPT login behind a routing proxy (subrouter): its sessions never start ("Authentication required"). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: Codex picks run the in-process bridge, not the npx codex-acp adapter With codex installed and no codex-acp on PATH, discovery kept the pinned npx adapter (@agentclientprotocol/codex-acp@1.10.0) as `codex-acp` beside the in-process bridge. The adapter reads the account through codex's account/read; with codex 0.162 a ChatGPT login behind a routing proxy (subrouter) fails that ("selected workspace missing from routing discovery"), so its chats never started ("Authentication required"), while the bridge works on the same login. Discovery no longer offers the npx adapter: an installed codex is the bridge, and `codex-acp` names the bridge too (like `codex-app-server`), so sessions, presets and model picks that recorded it run the bridge. A codex-acp on PATH, an ~/.acpx entry and a config-written launch are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: one description for the bridge's aliases (rustfmt) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cx-tgf4h review: forward chief control to a running brain first; daemon-side deny test; engine/stop CLI text; guard before side effects; fmt * test: warm the dev server in three agent pane browser tests (#20873) check:browser-lanes (#20733) requires warmViteServer after listen; #20752, #20780 and #20785 landed tests without it, so react-apps-check is red on base. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * acpmux tests: every acpmux process a test starts gets a hermetic environment (cx-s8wux) One helper, tests/support/daemon_env.rs, starts the acpmux binary for every integration test: env_clear, a bare PATH (python3 plus named host tools, then the system directories), a scratch HOME, and ACPMUX_AGENT_LAUNCH=deny unless the test drives a stand-in agent (Agents::StandIn keeps the bare PATH). The 26 test files that started the binary with the host environment use it; tests/hermetic_daemon_guard.rs refuses a test file that names CARGO_BIN_EXE_acpmux anywhere else. Root cause of cx-w014j: a fleet host's PATH holds real codex/claude, which a test daemon discovered and ran (probes, sign-in checks, auto-updates). * cx-tgf4h: fmt and clippy * acpmux tests: the hermetic HOME is <scratch>/home, never a chat's folder * acpmux tests: review fixes for the hermetic environment (cx-s8wux) python3 links the interpreter itself (sys.executable), not a version manager's shim; stale tool links from an earlier run are replaced; the guard also flags daemon_env::bin() outside a listed use; the live codex test probes only codex; codex_bridge_config's discovery test runs with launches denied. * cmux-tui: agent-session-attach takes the agent pane's whole kind list (up to 32; the pane asks 9 since prompt bookmarks), so a remote chat shows after Trust instead of falling back to New Tab with bad request (cx-kpd0v) * agent pane: the attach asks at most 8 kinds again (session offers come with transcript), so an older remote cmux-tui shows the chat (cx-kpd0v) * cmux-tui: test fake needs no mut (cx-kpd0v) * acpmux tests: router_install_secret starts the router through daemon_env (cx-s8wux) * cmux-next composer: keep base's catalog order; regenerate strings on the merged base Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: one typed protocol registry; generated web types and drift checks (hq#2025, no bead) (#20576) * acpmux: typed protocol registry and IR (hq#2025, no bead) One registry names every acpmux method and notification; sessions and permissions carry serde/schemars types, and the daemon builds SessionSummary and PermissionGroup from them. ir() emits spec/acpmux-protocol.json for the web codegen. Tests fail when the committed IR is stale or the dispatcher serves a name the registry lacks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: acpmux protocol codegen, typed request helper and drift check (hq#2025, no bead) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux protocol: committed IR, generated web types; permissions and session_changed use them (hq#2025, no bead) The permission group client and permission_respond go through the typed request; session_changed and permission_pending are narrowed by the registry's notification map. The IR declares a peer summary's optional fields and peer key, permission_pending's always-sent keys, and closed param objects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: format direct.ts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: acpmux codegen lives beside the pane protocol codegen, outside the pane sources Its developer-facing IR errors tripped pane-english, which scans src/agent-session/acpmux for user-visible English. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 1bb7576 - cargo fmt in Native/OptChat/optchat-chief Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38569912312 * chore(next): PR autofix at c601de5 - regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38593588859 * chore(next): PR autofix at 7a7fb66 - regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38606761975 * acpmux protocol: register offer_session and offer_respond Base (#20305) serves two new methods; the registry's completeness test caught them. Both are untyped requests for now. Spec and generated TS are regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux protocol: register prompt_withdraw Base serves _acpmux/prompt_withdraw; the completeness test caught it. Spec and generated TS are regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 4e1d3c9 - vp fmt webviews/src/pages/home-channels/HomeChannelsPage.tsx Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38637598208 * acpmux protocol: register the process methods Base serves _acpmux/process/list and process/kill and sends _acpmux/process_update; the completeness test caught them. Spec and generated TS are regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 64ca080 - vp fmt webviews/src/pages/home-channels/cloudHome.ts - cargo fmt in Native/OptChat/optchat-chief Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38649959912 --------- 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: fix two Chief profile relay base reds (concurrency check, Release compile crash) (no bead) (#20876) * cmux-next: mark the Chief profile relay retry loop as bounded (no bead) check-concurrency flags its while true as a loop that can spin. It retries once, only after a lost connection, and every other error throws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: nonisolated deinit for PageCallDeadline.Once (no bead) The Release compile crashes swift-frontend 6.3.3 in EarlyPerfInliner on the isolated deinit of this main-actor generic class (exit 65, no error line), the same crash #20408 fixed elsewhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(cmux-next): the Chief profile relay's retry is a two-attempt for loop, not while true with a wakeup-allow (cx-ebm.59.5) * cmux-next sidebar perf round 3: Release-build hitch bench with real wheel input; O(1) hover-card hit - scripts/cmux-next/sidebar-rows-perf-release.py: runs in a Release tagged app (no debug.* methods) inside nx-remote --needs input: CLI workspaces (50 with a terminal, 950 empty), HID-tap wheel events, Animation Hitches per gesture, A/B of two apps in one job (side by side, interleaved), traces and the recorder's ktrace files removed after each export, PTY count logged. - SidebarListView+HoverCard: the hover-card hit reads the list's row dictionary instead of SidebarModel.isPlaceholder, an O(workspaces) section walk on every scroll frame (7-12% of main-thread samples while scrolling 1,000 rows in Release). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels: a primary or danger button keeps its fill under the pointer (a soft ring shows the hover); gallery play primary-hover Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels gallery: primary-hover asserts the hover ring and the unchanged fill; a budget with a reason for the Create frame Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-motion: Chrome tab strip tween (200 ms easeOutQuad, restart from value), tabStrip geometry tokens, damping <= 0 takes the fallback (hq-1d, no bead) Additive: MotionTween {TabOpen, TabClose, TabMove}, TweenCurve::EaseOutQuad, ease_out_quad, TweenParameters, TweenValue (f64 core: retarget restarts the duration from the current value with no velocity; the same target does not restart), Tween (f32, token-driven), MotionPolicy::tween (0 under speed off, Reduce Motion, or tabOpen/tabClose with ui.animations.newTab off), TabStripGeometry (design-tokens.json motion.tabStrip: max 200, open/close 18, min 56 active / 32 inactive / 64 pinned, reorder 16 x width / 200, closing mode +40 below / +60 trailing / 0.3 s). cmux-app-ffi ABI 2 exports cmux_motion_tween_{base,seconds,retarget,step}, cmux_motion_ease and cmux_motion_tab_strip_geometry. SpringParameters::finite_or now treats a damping fraction <= 0 like NaN (hq-4e nit). The parity corpus example prints tween paths (60 Hz: 12 frames for 200 ms). * acpmux: record a session's git branch and HEAD at start (hq#1920) (#20840) * test(acpmux): record a session's git branch and head at start A session should keep its folder's branch and HEAD commit as they were at session/new, and a fork its own folder's state at fork time (hq#1920), so agent spend can be joined to the pull request a session produced. These fail today: neither the saved meta nor _acpmux/info carries gitBranch or gitHead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(acpmux): record a session's git branch and head at start session/new and session/fork now store the cwd's checked-out branch and full HEAD commit in the session meta (gitBranch, gitHead), so a session can later be joined to the pull request it produced (hq#1920). The values are read once, as the session starts, from the repository's own files (HEAD, loose refs, packed-refs, a linked worktree's gitdir and commondir), never a git process, and off the async runtime. A detached HEAD records only the head; a folder outside a repository records neither; a lookup that fails never fails the session. A fork reads its own folder at fork time instead of copying the parent's. _acpmux/info carries both fields. Old session.json files load unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(acpmux): git state skips non-files, bad branch names and missing folders; follows symlinks Review of #20840: a FIFO HEAD hung session/new, a symlinked folder was walked lexically, a missing fork folder took its parent repository's state, and an invalid ref name was stored as the branch. The test git also runs without the machine's global or system config. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): git state reads only small regular files, the physical folder and valid ref names Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cx-tgf4h: keep CmuxNextApp line count (App size cap) * acpmux: a pooled agent host no longer spins on Linux reading its own record (cx-7z8yx) The record watch used notify's inotify backend, whose watch mask always includes open, close-write and modify, and ran its callback on every event. The callback reads the host record; on Linux that read queued an open and a close in the watched directory, which ran the callback again. Every pooled host spun at 100% CPU (about 200,000 read calls a second). Linux now has its own inotify watch, like the macOS kqueue watch: a mask of entry changes only (create, delete, moved from/to) and the directory's own delete or move, one thread in poll with no timeout, an eventfd to stop it. A read of the record never queues an event. The notify fallback (other platforms) ignores access events. macOS is unchanged. Behavior test (tests/host_lifetime.rs, Linux): a real daemon pools a host; idle for 3 s it makes fewer than 100 read calls and 30 CPU ticks (before: 599,042 reads, 298 ticks), a rename rewrite of its record keeps it, and the record removed ends it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-tui smoke: sidebar drag checks the drawn sidebar document order Since sidebar-v2 S6 the TUI draws the daemon's sidebar document, where a new workspace goes to the top (workspaces.newPlacement default), and a workspace drop is one sidebar.move that changes the document only. The smoke still assumed the list-workspaces order: it dragged the drawn top row (the new workspace) below the original, which the product did, and then read list-workspaces, which a sidebar.move never changes. The smoke now reads the order from sidebar.get, checks that the rail draws it, drags the top row below the second (a real reorder), and drags again when needed so the original workspace ends below the new one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * PR dock chips; tooltips only where nothing else names the control (#20375) * test: no tooltip on an element whose text shows Red on base: 105 elements with visible text carry title/data-tooltip. Command: bun test test/tooltip-rules.test.ts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: PR dock chips; tooltips only where nothing else names the control The pull request dock's bars become content-sized chips (icon, #N, repo, title cut off at 360 px) with the dismiss button inside and no tooltip. Across the pages, a row, chip, link, path or thought whose text shows carries no title tooltip any more; tooltips stay on icon-only controls, shortcuts and text shown nowhere else (why a control is disabled or failed, who reacted, policy detail), each marked tooltip-allow. Tooltips wait the native hover delay (about a second) and show at once when moving straight on to the next one. Green: bun test test/tooltip-rules.test.ts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: review of tooltip audit Keep the PR dock's state colours, drop four more tooltips the lint now finds inside components (a setting's summary, the branch and folder pickers, a video), let a click or scroll end the quick next-tooltip window, and move two tests to the no-tooltip rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: Backdrop Art's line has no tooltip Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 047c247 - vp fmt webviews/src/pages/history/HistoryPage.tsx Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38502564051 * test(mock): the worked chat opens its pull request, so mock mode shows the PR dock Captures of the agent pane in a real window (--showcase runs the in-page mock) now show the pull request dock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: the PR dock takes its own space; the transcript ends above it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 39f3ca9 - vp fmt webviews/test/ime-composition-keys.test.ts - vp fmt webviews/test/new-tab-omnibar-pointer.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38567629461 * chore(next): PR autofix at c7cb261 - regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38592980576 * fix: a channel image keeps its name tooltip (the name shows only when it fails) Base added title={part.name} on channel image attachments (AttachmentViews.tsx). A loaded image shows no name, so the tooltip is its only label; mark it with tooltip-allow. tooltip-rules.test.ts: red before, green after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at f221864 - regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38633116142 * fix: keep the tool card scope hint and Steer shortcut tooltips Both explain something shown nowhere else, so they stay under tooltip-allow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 022cc8e - vp fmt webviews/src/pages/home-channels/cloudHome.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38647981824 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com> * ci: nightly-next recovery initializes the notices submodule before continuing accepted submissions The recovery job's checkout had no ghostty-next submodule, so the bundle notice check that notarize-nightly-dmg.sh runs after Apple accepts failed, and run 38669958906 skipped five accepted builds as 'Apple did not accept'. Initialize the submodule like the signing job does, and make the skip notice say that a post-acceptance check can also be the cause. * ci: the cmux NIGHTLY-identity job also initializes the notices submodule Run 38669958906's publish-nightly-identity-legacy job saw Apple accept, then failed the bundle notice check for lack of ghostty-next and retired the accepted submission of run 38609024986. * agent pane: progress and stop for a running Claude subagent or workflow (cx-liswe) (#20718) * test: a running subagent or workflow shows what it does and what it has spent Red on base: both rows read only "Running" (bun test test/subagent-progress.test.tsx). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * webviews: a running subagent or workflow shows what it does and what it has spent The subagent fold keeps acpmux's subagent_progress fields (action, tokens, tool uses, a workflow's agents done of started), and the transcript's subagent row adds them to its status line through one shared helper (subagentProgress.ts) the summary list and the Background tasks pane can use. Three plural strings, translated for every pane locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a running subagent row has a stop button Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat: stop a running Claude subagent or workflow from its row Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: restore the catalog's base order after the merge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: stopping a subagent leaves its row closed, and a refused stop says so Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: a subagent's stop button waits for Claude and says when the stop failed The button is off while Claude has the request and until the subagent ends; a refused or unanswered stop (10 s) turns it into "Couldn't stop <name>" and it can be tried again. The pane no longer drops the error. Also moves the stop wiring above showSubagent's doc comment and corrects two comments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * style: format the subagent progress test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: stop rejects without English the pane would never show Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: restore the catalog's base order after the merge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 2fe6cc4 - vp fmt webviews/src/pages/home-channels/HomeChannelsPage.tsx Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38637059713 * test: the agent pane relays a subagent stop for its chat (cx-liswe) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: the agent pane may stop a subagent of its chat (cx-liswe) Adds _acpmux/subagent_stop to the pane's method allowlist: session-scoped, params sessionId and subagentSessionId, no gesture (it stops work, like _acpmux/kill). Without it the host refused every stop click. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: mirror the subagent stop into the shared pane policy (cx-liswe) The Swift allowlist took _acpmux/subagent_stop; policy.json, its parity source for Rust hosts, gets the same request, session scope and params, with cases for an in-scope stop, another chat and an unknown param. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * gallery: a light window with no theme named renders light (#20794) * test(gallery): a light window with no theme named should show the light default Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(gallery): a light window with no theme named shows Apple System Colors Light The gallery defaulted the theme to the dark side of the app's default pair whatever the window appearance, so colorScheme=light (or Appearance: Light) rendered every stage in dark colors under a light media query. The default now follows the appearance, as the app's does with no theme set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- 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> * Channels view: gallery plays for a message link with no link read (red) A Cloud conversation has no link resolver (G4), so the Messages view's switch hand-off #/conversation/<id>/message/<id> showed an error. Two plays: a thread reply and a timeline message, opened through the store's openLink with an owner that has no resolver. cx-hnmbt Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view: open a known conversation's message link without the owner's link read When conversation-link-resolve fails (a Cloud conversation, an older daemon) and the page knows the conversation, it opens it, pages the timeline until the message shows, opens its thread when it is a reply, and marks it. The Messages view's switch hands off Cloud selections this way. cx-hnmbt Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view: format parseConversationLink (vp fmt) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Channels view: link fallback review follow-ups Ids in a known conversation's link may hold '-' (the gallery's sample ids); the scheme check follows the daemon (cmux[-<tag>], any case); the fallback runs only for a Cloud conversation or an owner without a link read, so a refused local link (a deleted message) still shows its error. cx-hnmbt Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * acpmux: host_lifetime idle test cleans up from the start and never signals a reused pid (cx-7z8yx review) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * cmux-tui: move the build identity into a leaf crate so an edit no longer recompiles cmux-remote cmux-remote's build script hashed the dirty source tree into CMUX_TUI_BUILD_IDENTITY, so every edit in the workspace reran it and recompiled cmux-remote and every crate above it (about 8 s of a 12.6 s one-line edit loop on rbx). The same build script now lives in cmux-build-identity, which only cmux-tui reads; cmux-remote takes the identity as an argument (SshBootstrapConfig::defaults, pinned_linux_payload). The identity value, its inputs and format, and every skew and SSH re-bootstrap comparison are unchanged. * test: warm the dev server in the Claude reasoning default browser test (#20884) #20592 landed after #20873 without warmViteServer, so check:browser-lanes is red on base again. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * cmux-next: frame-strip report as an NSWindow method, not a namespace enum (no bead) (#20887) The package conventions lint fails every PR on the new all-static public enum WindowFrameStrip (cx-me3xd). The report reads its window, so it is an extension on NSWindow; same body, same report. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Home channels: the run card is an inline disclosure (no frame, steps on an indented rail) The live and done run cards under a Chief's reply lose their box: "Working for" / "Worked for", the step count, cost and Open sit inline under the message, and the opened steps hang on a thin rail under the status mark. The narrow rules stay: the first line never wraps, and below 480 px of card width the step count and cost move under it with no leading separator. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Home channels: the run rail and columns follow the author's picture Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Home channels: the run sheet keeps its error line; run card columns in logical properties Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Revert "cmux-motion: Chrome tab strip tween ..." (8a3ee62) until the app-ffi release App 403 is fixed (run 38682698310) (hq-1d, chief go 2026-10-15, no bead) The release App could not publish cmux-app-ffi-854febb26527 (HTTP 403 on POST /releases), so the app FFI pin check stays red on the tip. The tween re-lands with the publish and repin after the App is fixed. * test(ci): nightly jobs that run the license check must init ghostty-next (#20893) * test(ci): nightly jobs that run the license check must init ghostty-next Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ci): nightly recovery initializes ghostty-next before the license check Apple accepted, stapled and launch-tested each recovered nightly-next build, then verify-app-bundle-licenses.sh failed on the missing ghostty-next/build.zig.zon and the build was skipped as not accepted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep catalog in base order after feat merge 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: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Co-authored-by: safe-push <safe-push@cmux.local>
…the protocol registry; pane policy test reads typedRequest (cx-js8uc) protocol_registry_names_every_served_method was red on every OS: _acpmux/harness/update and _acpmux/sessions/restart_harness (#20569, cx-1mbnu) and now _acpmux/btw (#20847) were served but not declared. Declare them (untyped, Request), refresh spec/acpmux-protocol.json and the generated web methods, and document update/restart_harness in docs/acpmux-schema.json. the_allowlist_is_what_the_page_sends was red since #20576: direct.ts sends _acpmux/permission_respond through typedRequest(send, "...", ...), which the test's this.request( scan missed. The test now also reads the literal method of each typedRequest call. policy.json is unchanged: the page still sends permission_respond, and the page never sends the harness methods itself (the Swift host calls them over the unix socket, gesture-gated; acpmux refuses them for LocalApp, Web and peer). Remove both cx-js8uc rows from scripts/ci/cmux-tui-base-red.tsv.
…mbnails (#20411) * test: a prompt's images are named in its message and read back after a restart Red on base: user_message carries only text, and _acpmux/attachment does not exist. Command: scripts/verify-cmux-tui-hosted.sh --filter a_prompts_images Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): keep a prompt's images and name them in its user_message Each image block's bytes are kept once by SHA-256 under <ACPMUX_HOME>/attachments; user_message (prompt and steer) records images: [{id, mimeType, name}], and _acpmux/attachment {sessionId, id} reads one back for a session whose log names it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a sent prompt keeps its images in the pane Red on base: the sent bubble shows text only (no .cv-user__images thumbnail). Command: CMUX_BROWSER_TESTS=1 bun test test/agent-pane-sent-images.test.ts The pane's mock transport keeps and names prompt images as acpmux now does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(webviews): show a sent prompt's images and keep them on edit and retry A prompt's bubble shows the images its user_message names, read back with _acpmux/attachment and cached by id; each opens the image viewer. Edit, Retry and Retry with another model send the prompt's images again, read back from acpmux when this page did not send them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: format the sent-images test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): keep the god files from growing with prompt images append_prompt records a prompt's user_message with its images, and _acpmux/events and _acpmux/attachment move to server/requests/events.rs, so hub/turns.rs and server/requests.rs end smaller than on the base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: the pane relays a sent prompt's image read to acpmux Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(agent pane): relay a sent prompt's image read to acpmux The pane's host transport refused _acpmux/attachment (default deny), so the app showed no sent images and Edit/Retry after a reload lost them. It is a read like _acpmux/events; params sessionId and id only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(webviews): sent image checker takes its color from the theme Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(webviews): sent image thumbnails get the composer's clear edge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: a dark screenshot's composer thumbnail is big enough and keeps a clear edge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(webviews): composer image thumbnails keep their shape, bigger, with a clear edge A thumbnail was a 56 px center-cropped square, so a wide dark screenshot showed as a sliver of its text, behind a hairline of the border color. It is now 80 px tall in the image's own shape (0.7:1 to 2:1) with a 1 px edge in the text's color. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(webviews): thumbnail height is its own so WebKit keeps the shape; close the bitmap Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(gallery): attach-screenshot's budget is the 80 px thumbnail row Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 59fec84 - cargo fmt in Native/OptChat/optchat-chief Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38570048011 * chore(next): PR autofix at 0acad51 - regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38593540654 * chore(next): PR autofix at a00a980 - regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38611220635 * docs: keep OrderedRows' whole doc comment on its class Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 92aca21 - vp fmt webviews/src/pages/home-channels/cloudHome.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38648933021 * test: warm the dev server in the sent-images browser test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux protocol: register _acpmux/attachment Base's protocol registry (#20576) lists every method the daemon serves; this PR's attachment read joins it, untyped like _acpmux/events. Spec and generated web method table updated to match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: keep the attachment allowlist inside AcpmuxPaneMethods' line budget The god-file check failed: the file grew from 403 to 407 lines (limit 400). Fold _acpmux/attachment into the extensions line with a one-line 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>
…t parity test (cx-ctddg) (#20947) * test: the pane policy's requests and params are the macOS host's (red) policy.json lags AcpmuxPaneMethods.knownParams: session/new noProject, session/prompt context, _acpmux/models refresh and _acpmux/permission_respond _meta. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: the Rust pane policy matches the macOS host's params policy.json gains what AcpmuxPaneMethods.knownParams already allows: session/new acpmux noProject, session/prompt acpmux context, _acpmux/models refresh, and _acpmux/permission_respond _meta, which holds the pick's gesture ticket and nothing else (the same rule as Swift's breaksParamsRule). The allowlist test also reads the method of direct.ts's typedRequest calls (#20576 moved permission_respond there). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at d1deeab - regenerated schemas/defaults/cmux-next.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38699935874 * test: a ticketed permission answer's pick names its session; answers pick nothing (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: a permission answer's gesture pick names its session (review fixes) GesturePick::new gains Swift's AgentPaneGesturePick branch: a _acpmux/permission_respond picks {sessionId, permissionId, optionId}, and nothing when it carries answers. lib.rs says hosts strip a session/new's _meta.acpmux.noProject in their folder step. The deleted AgentPanePolicyParityTests are no longer cited (policy.json, lib.rs, the Swift checkOne comment). The typedRequest scan reads the call's own literal second argument or fails. Two more permission_respond _meta cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: keep base's defaults export (the autofix added a key this PR does not touch) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: record Lawrence's existing key.home.switchView default in the defaults snapshot Brings back the autofix hunk: it is not a new default, it fixes the base defaults-snapshot red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: keep base's defaults snapshot (the key.home.switchView record is base's to land) 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>
… and _acpmux/event (#20894) * acpmux protocol: type attach, events, detach, view, markUnread, watch and _acpmux/event PR2 of the typed acpmux protocol (PR1 #20576). The registry types the session-log methods and the `_acpmux/event` notification (protocol/events.rs): AttachParams/AttachResult with SessionDetail (the summary plus what an attached client draws), EventsParams/EventsResult, SessionRef, ViewParams, WatchParams and SessionEvent. `server::event_value` builds its record from SessionEvent, so the wire shape has one source. `_acpmux/sessions` and `_acpmux/watch` page with limit and cursor since PR1 landed; SessionsParams and SessionsResult now say so (nextCursor). Also registers harness/update and sessions/restart_harness, which base serves now. The web client sends these methods through a typed helper, and the pane's EventRecord derives from the generated SessionEvent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux protocol: SessionDetail carries every SessionSummary field again SessionDetail flattens SessionSummary, but the committed IR still had the summary of Oct 14: base has since added children, depth, lifecycleReceipt, mcpServers, pullRequests, shortName, spawnedBy and startedBy. The IR's SessionDetail is now the current summary plus its own fields, and the web types are regenerated. direct.ts imports the generated types once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray * acpmux protocol: SessionDetail takes base's backgroundsWork Merged feat-cmux-next (#23211 added backgroundsWork to SessionSummary); SessionDetail flattens it, so the IR and the web types carry it too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray * tests: drop the registry line a local hook added in the base merge tests/test_media_auto_tour.py is base's (unregistered there); registering it belongs to the base, not to this PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>






Summary
First step of a typed acpmux protocol (plan: manaflow-ai/cmuxterm-hq#2025; T3 Code's
packages/contractsis the model). Rust is the source of truth, and the web gets generated types plus a typed request, with drift checks on both sides.cmux-tui/crates/acpmux/src/protocol/)._acpmux/*,catalog.*, chats, transfer), with their kind._acpmux/sessions,_acpmux/permission_respond,_acpmux/permission_groups,_acpmux/permission_group_respond,_acpmux/permission_chat_revoke, and the_acpmux/session_changedand_acpmux/permission_pendingnotifications.Hub::session_summarybuildsSessionSummary, and permission groups buildPermissionGroup.json!output; no key, null or number changes.json!panic.spec/acpmux-protocol.jsonholds JSON Schema 2020-12 from schemars.webviews/scripts/acpmux-protocol-codegen.tsgeneratesagent-session/acpmux/protocol/generated/: one type per schema, plusAcpmuxRequests,AcpmuxNotificationsandACPMUX_METHODS.protocol/client.tsaddstypedRequestandisNotification.permission_respond, and thesession_changedandpermission_pendinghandlers now use them, and the hand-written permission types alias the generated ones.protocol_ir_is_currentfails on a stale IR (bless withACPMUX_PROTOCOL_BLESS=1).protocol_registry_names_every_served_methodfails whenrpc::methodor a server literal names a method the registry lacks.bun run checknow ends withacpmux-protocol:check, which fails on stale generated TS.Follow-ups in hq#2025: type the rest (attach, detach, view, events, watch, then everything else); serve the IR from
_acpmux/schemaand retire the hand-writtendocs/acpmux-schema.json; Swift Codable.Changelog
none
Verification
All of this ran on the private Linux builder, on the merged head (base 93d4ef2):
cargo test --locked -p acpmux --lib protocol_: 3 passed.cargo clippy --locked -p acpmux --all-targets -- -D warnings: clean.bun run checkin webviews: green, including the new drift check and the typecheck of 1318 files.bun test src/agent-session/acpmux/protocol: 4 pass.The crash ratchet is ok.
No UI change.
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Turns
acpmuxinto a typed protocol: one Rust registry names every method and notification, and the web gets generated types plus a typed request from it.session_changedandpermission_pendingnotifications; the rest are any JSON.SessionSummaryandPermissionGroupfrom the types; a non-UTF-8 cwd is now lossy instead of ajson!panic.lane,inputSince,lastTurn.detail) and chat tools (tools,toolsMutable); the workspace field was dropped again with the branch's last merge.spec/acpmux-protocol.jsonholds the JSON Schema 2020-12 IR; codegen lives beside the pane protocol codegen, outside the pane sources, so pane-english does not scan its developer-facing errors.typedRequestandisNotificationhelpers.ACPMUX_PROTOCOL_BLESS=1); they caughtoffer_session,offer_respond,prompt_withdraw, the tools, stats, bookmark, and process methods, now registered as untyped requests._acpmux/schema, add Swift Codable.<pull_request_end>
Written for commit 563926c. Summary will update on new commits.