Repository navigation
[CMUX Next] Chief pet: the daemon refuses a reply once its agent left the foreground - #22044
Conversation
A small Chief character in a non-activating panel that joins every Space, never takes focus, drags anywhere (its spot is kept per display and pulled back on screen), and opens the Chief on a click. Behind the chiefPet.enabled Debug Settings toggle, off by default, until it has a user setting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CmuxNextApp may not grow (code-quality SW14), so the pet lives in a new CmuxNextFloating target with the floating chat's per-display frames, which it already shares; the App keeps only the wiring and shrinks by 23 source lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39137151099
A public default argument may only use what the file imports publicly; the controller now takes an optional switch and reads the tunable itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The App's ChiefAvatar.png is the round cat Home draws in a disc; Leo's references float the full-body Chief the Chief Dock uses. CmuxNextFloating now bundles that character (ChiefPet.png, the dock's ChiefAvatar.png) as the pet's default image. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit 2eed332.
Match chief-water-reference: the Chief Dock's pixel mascot (Orb by default, all nine bundled, scaled up with whole pixels) over a glass rail with a compose tool, 8 pt apart. A drag anywhere but a tool moves the pet; a click on the character or compose opens the Chief. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rail matches chief-water-reference's capsule: compose and the chevron (the dock's disclosure mark pointing up). The character's box is 88 x 80 pt so the pixel mascot carries the reference pet's weight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What alerts on this Mac (a banner's rules) also shows over the pet, matching chief-chat-source-captures d154d558: glass capsules of the references' size (330 x 56 pt) with a bold title and one muted line and no status glyph, the older ones peeking out below as a deck; the rail's chevron unfolds them into a column and each capsule opens its tab. One notice per tab; identical content never re-animates; a finished turn leaves after 8 s while a question or failure stays, counted on the character's badge, until its tab is read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A click on the pet or the rail's compose opens a reply capsule just over the pet, the notices' size and glass, that takes the keys without activating cmux. It starts on the newest notice: Return types the reply into that terminal (as a notify --reply banner does) or, for an agent chat, opens the chat in the floating chat or its tab; with no notice it messages the Chief. Cmd-Return opens the target. The Quick Agent Chat's panel moves into CmuxNextFloating with the rest of the floating surfaces, so the App keeps shrinking. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ettings home.chiefPetEnabled (off by default) and home.chiefPetMascot (the Chief Dock's nine pixel mascots, Orb by default) in Settings > Home > Chief Pet, the palette's settings rows and cmux settings set replace the Debug Settings toggle; agents may set both (looks only). The pet follows them live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Replies type into a terminal only while the notice is still its tab's unread notification, as a notify --reply banner's do. - A notice read anywhere else (another client, a closed tab) leaves the pet. - The reply menu keeps two notices with one title apart, keeps the chosen session across refreshes and falls back to the Chief, never to another session; Cmd-Return opens, other command keys stop at the box. - Notice capsules take their press, so a click opens the tab; an agent chat opens its tab rather than a second copy in the floating chat. - A finished turn dropped behind three waiting notices shows nothing. - CmuxNextFloating no longer imports CmuxNextDaemon (the App maps the notification); the pet is made by the notification service, so AppServices and AppDelegate stay unchanged; the mascot constant is initial (lint); the Chief Pet settings get a card under Agents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pet-setting # Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextSettings/Schema/SettingsPageLayout.swift
…plies, no screen sharing - home.chiefPetEnabled is user-only: the pet puts agent output over every app and a reply box that types into terminals. The character stays agent-settable. - A pet reply types only while a program still runs in the terminal's foreground: process-info and the paste go over one connection, back to back, and a bare shell refuses the reply. - The pet, its notices and the reply box stay out of screen shares. - The notice deck takes its size with no screen, and the deck height test compares CGFloats. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r the Chief pet Generated only. Settings exports from the fleet export-settings step at 2f2f1e6 (cmux-ci step 20d9ccba77afe7b51e35de31, decoded from its BEGIN_ARTIFACT blocks), defaults snapshot from defaults_guard.py snapshot, Settings page strings from webviews/scripts/pages/gen-strings.mjs settings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pet-setting # Conflicts: # schemas/settings-export/settings-schema.sources.sha256 # schemas/settings/settings-schema.json
…ef pet test feat-cmux-next moved NotificationPolicy into CmuxNextSettings (#21735). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…base merge Generated only, from the fleet export-settings step at 492302f (cmux-ci step on key chiefpet-export-settings-492302f); defaults snapshot and Settings page strings checked fresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pet-setting # Conflicts: # Packages/macOS/CmuxNext/Package.swift
send with expect_foreground is ignored today, so a Chief pet reply typed after its agent exited runs as a shell command. This test is red until the daemon checks the foreground in the send. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pet-setting # Conflicts: # Packages/macOS/CmuxNext/Package.swift # Packages/macOS/CmuxNext/ci-target-graph.json # schemas/settings-export/settings-schema.sources.sha256 # schemas/settings/settings-schema.json
…base merge Generated only, from the fleet export-settings step at b6e4dd2 (key chiefpet-export-settings-b6e4dd2); defaults snapshot and Settings page strings checked fresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39189337102
…pet-setting # Conflicts: # schemas/settings-export/settings-schema.sources.sha256 # schemas/settings/settings-schema.json
…t/chief-pet-setting
…base merge Generated only, from the fleet export-settings step at 5bedbf4 (key chiefpet-export-settings-5bedbf4); defaults snapshot and Settings page strings checked fresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pet-setting # Conflicts: # schemas/settings-export/settings-schema.sources.sha256 # schemas/settings/settings-schema.json
…base merge Generated only, from the fleet export-settings step at c1475df (key chiefpet-export-settings-c1475df); defaults snapshot and Settings page strings checked fresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generated SDK bindings regenerated with cmux-tui/bindings/codegen/generate.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…const Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Medium impact] The change reaches terminal input in the Chief pet workflow and adds an optional send contract for protocol clients. The PR should not merge yet because a guarded reply can still reach the shell after the terminal-host connection waits or queues input. Findings
SummaryThis PR adds an optional foreground check to terminal input.
DiagramsequenceDiagram
participant App
participant Daemon
participant Host as Terminal host
participant PTY
App->>Daemon: process-info
Daemon-->>App: Foreground executable
App->>Daemon: send with expect_foreground
Daemon->>Daemon: Compare current foreground
alt Foreground differs or cannot be read
Daemon-->>App: Refuse without writing
else Foreground matches
Daemon->>Host: Send input bytes
Host->>PTY: Write input
end
Reviews (5) · Last reviewed commit: "cmux NEXT: the guarded pet reply lives o..." · Reviewed by Greptile |
| if let Some(expected) = expect_foreground { | ||
| let found = surface.process_id().and_then(platform::foreground_process_name); |
There was a problem hiding this comment.
The foreground check runs before the terminal host receives the input. write_paste and write_bytes send bytes over another connection. send_hosted_input can wait up to five seconds for that connection to recover, then retry without checking the foreground again. If the agent exits during that wait, the reply reaches its shell despite expect_foreground.
Even without reconnecting, both quick-reply requests can pass their checks before the host consumes their queued frames, letting the shell run the reply as a command. Carry the expectation to the PTY owner and check it after any wait, immediately before writing.
Knowledge Base Used: TUI client and relays
- regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39313213321
…pet-setting # Conflicts: # schemas/settings-export/settings-schema.sources.sha256
…h (Linux autofix's output) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Same 15pt title size and semibold weight, now from TypeScale, so the Type scale check no longer flags the pet's files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The app lists send-expect-foreground-v1 as an optional capability and notes an unguarded reply when an older daemon lacks it. - expect_foreground with terminal_id is refused as not delivered, like the other send refusals. - The spec says the check is best effort, with a residual window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Correctness-first review (opus subagent) at f336cfc: SHIP with nits, no blockers. Each nit is fixed at ee8ad50:
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ef-pet-daemon-guard # Conflicts: # cmux-tui/bindings/cpp/.cmux-sdk-manifest.json # cmux-tui/bindings/cpp/include/cmux/raw/generated/models.hpp # cmux-tui/bindings/go/raw/.cmux-sdk-manifest.json # cmux-tui/bindings/go/raw/generated_commands.go # cmux-tui/bindings/go/raw/generated_events.go # cmux-tui/bindings/go/raw/generated_metadata.go # cmux-tui/bindings/go/raw/generated_presence_test.go # cmux-tui/bindings/go/raw/generated_types.go # cmux-tui/bindings/java/src/com/cmux/raw/.cmux-sdk-manifest.json # cmux-tui/bindings/java/src/com/cmux/raw/Protocol.java # cmux-tui/bindings/python/cmux/raw/_generated/.cmux-sdk-manifest.json # cmux-tui/bindings/python/cmux/raw/_generated/_schema.py # cmux-tui/bindings/python/cmux/raw/_generated/metadata.py # cmux-tui/bindings/rust/src/generated/.cmux-sdk-manifest.json # cmux-tui/bindings/rust/src/generated/commands.rs # cmux-tui/bindings/rust/src/generated/events.rs # cmux-tui/bindings/rust/src/generated/metadata.rs # cmux-tui/bindings/rust/src/generated/mod.rs # cmux-tui/bindings/rust/src/generated/types.rs # cmux-tui/bindings/typescript/src/raw/generated/.cmux-sdk-manifest.json # cmux-tui/bindings/typescript/src/raw/generated/commands.ts # cmux-tui/bindings/typescript/src/raw/generated/events.ts # cmux-tui/bindings/typescript/src/raw/generated/index.ts # cmux-tui/bindings/typescript/src/raw/generated/metadata.ts # cmux-tui/bindings/typescript/src/raw/generated/types.ts # cmux-tui/bindings/zig/src/raw/generated/.cmux-sdk-manifest.json # cmux-tui/bindings/zig/src/raw/generated/protocol.zig
…aemon-guard # Conflicts: # Packages/macOS/CmuxNext/Package.swift # Packages/macOS/CmuxNext/Sources/CmuxNextApp/Notifications/NotificationCenterService+ChiefPet.swift # schemas/settings-export/settings-schema.sources.sha256 # schemas/settings/settings-schema.json
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. |
… lane knows expect_foreground typeToForegroundProgram does the foreground check and the two guarded writes in CmuxNextDaemon, so the App target shrinks. The regenerated phone-lane field list allows expect_foreground on send: the phone lane already has send and process-info, and the field can only refuse a write. 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. |
|
Shipped in:
|
The Chief pet's quick reply (#21738) checks with
process-infothat an agent, not a bare shell, is in the terminal's foreground, and then pastes the reply and presses Return. If the agent exits between that check and the paste, the shell runs the reply as a command. Now the daemon does the check itself, right before it writes.sendgets an optionalexpect_foregroundstring, behind the new capabilitysend-expect-foreground-v1. Just before writing, the daemon reads the surface's foreground process-group leader again (foreground_process_name, the same valueprocess-inforeports asforeground_executable). If that leader isn't the named executable, or can't be read, the request fails withforeground changed: ..., classified as known-not-delivered, and nothing is written.expect_foregroundonly works withsurface. Combined withterminal_id, the request fails withbad request, because a terminal that's still being created has no foreground yet.On the App side,
SendInputRequestandDaemonConnection.sendgainexpectForeground, andDaemonConnection.typeToForegroundProgramdoes the check and the two guarded writes, so the App target shrinks.typeChiefPetReplycalls it and keeps itsrunningProgramcheck as the first gate, then passes theforegroundExecutableit saw on both writes, the paste and the Return.Closes the follow-up coord-2 asked for when it reviewed #21738. Stacked on #21738: the base is
feat/chief-pet-setting. Retarget tofeat-cmux-nextonce #21738 merges.Compatibility
The
sendcommand enum doesn't deny unknown fields, so an older daemon ignoresexpect_foregroundand writes as before. The App's own check still protects that case, with the old small race. Swift leaves the field off when it's nil, so unguarded sends are byte-for-byte unchanged (tested). The spec and all seven generated bindings carry the new field (generate.py --write;--check,check-spec-inventory.pyandcheck-resource-api-boundary.pypass).Socket policy analysis
sendalready writes input to a terminal, and this field can only make it refuse a write it would otherwise make. The remote relay still refusessendbefore parsing it (commands.md), so no relay path changes.surfacewith the same authority as before.sendauthority can already read it throughprocess-info.DaemonLanePolicy+Fieldsnow allowsexpect_foregroundonsend. The phone lane already hassendandprocess-info, so this field adds no read, and it can only make the daemon refuse a write.Verification
cli/send_expect_foreground.rs. Hosted run 39179652753 failsa_reply_for_an_exited_program_never_reaches_the_shellon Linux and macOS withthe reply reached the shell: {"data":{},"id":4,"ok":true}.cargo check -p cmux-sdk --all-targets) builds. A re-run on the merge head 8765203 is in progress.SendInputRequestTests(the encoded wire hasexpect_foreground, and an unguarded send omits it). No Swift toolchain on the authoring box, so these tests and the App change are compiled and run only by this PR's CI.verify-local.py --affected: every selected check passes exceptlocalization, which fails with the base branch's existingneeds_reviewcatalog entries. This PR changes no strings.sleepexits via Ctrl-C, then a guarded paste is refused and never reaches the shell.Changelog
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Closes the race where a Chief pet quick reply was typed as a shell command when its agent exited between the app's foreground check and the paste. The daemon now verifies the foreground itself, right before it writes.
sendgains an optionalexpect_foregroundfield behind the newsend-expect-foreground-v1capability. Just before writing, the daemon re-reads the surface's foreground process-group leader; if it isn't the named executable, or can't be read, the request fails withforeground changed: ..., writes nothing, and is classified as known-not-delivered. The app names the executable its existingprocess-infocheck saw on both the paste and the Return writes, and treats the capability as optional so an unguarded reply is still noted when an older daemon lacks it.Older daemons ignore the field, leaving unguarded sends byte-for-byte unchanged. The field requires
surface; combined withterminal_idthe request fails as not delivered. The check is best effort: the program can still exit between the read and the write, a window of microseconds instead of a client round trip.Tests
sleepexits via Ctrl-C, and the foreground check covers a running program,terminal_id, and an unreadable foreground.expect_foreground.Written for commit 4cfeafc. Summary will update on new commits.