Repository navigation
Add a Stop button over terminals while an agent turn runs - #17729
azooz2003-bit wants to merge 31 commits into
Conversation
Behind the new agentActions.turnControl setting (off by default), a Stop pill appears in a terminal pane's bottom-trailing corner while Claude Code or Codex is running a turn there. Clicking it sends Escape through the same named-key path the TextBox uses, then journals the interrupt as a turn completion for the pane's active hook session: agents run no Stop hook when interrupted, so the pane would otherwise stay running. Part of #15223. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review fixes for the Stop button: - The interrupt now settles every session the journal consumer has running on the surface (AgentLifecycleReducerState.userInterruptDrafts), instead of guessing the newest hook-store entry. A nested `claude -p` session or a relay session no longer gets a phantom idle session while the parent keeps running, and a click no longer parses the hook store on main. - Only Claude Code settles through the journal. Codex journals lifecycle at prompt submit and Stop only, so a settle could leave a still-working Codex pane idle for the rest of its turn. - Stop holds for 1.5 s after a click, so a double click sends one Escape (a second Escape at Claude's idle prompt opens its rewind menu). - The pill is raised above the find overlay whenever that overlay is. - tests/test_cmux_settings_supported_paths.py knows the agentActions section. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # cmux.xcodeproj/project.pbxproj
Journals a Claude Code turn on the focused terminal over the control socket, checks the Stop pill appears, clicks it and checks it hides, and checks the pill stays hidden with agentActions.turnControl off. Keeps before/after window screenshots. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
surface.current never answered in CI; workspace.create returns the new terminal's ids directly, with the netcat socket fallback. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # cmux.xcodeproj/project.pbxproj
Both CI attempts had the socket answering ping while main-thread verbs timed out right after launch. Retry activate_app like the focus-history test before creating the terminal. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The fixture harness's /tmp socket passed its readiness check through the app's own diagnostics while every command from the test runner came back empty. Put the socket in the runner's temporary directory, as the hook prompt-length test does, and talk to it over netcat. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The argument domain stored "YES" as a string, which the settings catalog does not decode, so the pill stayed off. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # cmux.xcodeproj/project.pbxproj
Catch-up merge by scripts/ci/catch_up_pr.py (RFC #14631). Merged by scripts/merge-main.sh: mf/main at da27bbc. Resolved conflicts: - Resources/Localizable.xcstrings: xcstrings key-level union - cmux.xcodeproj/project.pbxproj: union of added entries, then normalize-pbxproj.py - Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/ConfigValidation/CmuxConfigSchema.generated.swift: generate-cmux-config-schema.py, regenerated from the merged schema (both sides changed the schema) Catch-up-previous-head: b3b7a50 Catch-up-base: da27bbc
Catch-up merge by scripts/ci/catch_up_pr.py (RFC #14631). Merged by scripts/merge-main.sh: mf/main at 8b23dd7, the newest commit with green CI fast guards (2 newer skipped). Resolved conflicts: - Resources/Localizable.xcstrings: xcstrings key-level union - cmux.xcodeproj/project.pbxproj: union of added entries, then normalize-pbxproj.py - Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/ConfigValidation/CmuxConfigSchema.generated.swift: generate-cmux-config-schema.py, regenerated from the merged schema (both sides changed the schema) Catch-up-previous-head: 0156a4a Catch-up-base: 8b23dd7
Catch-up merge by scripts/ci/catch_up_pr.py (RFC #14631). Merged by scripts/merge-main.sh: mf/main at 0abac32. Resolved conflicts: - Resources/Localizable.xcstrings: xcstrings key-level union - cmux.xcodeproj/project.pbxproj: union of added entries, then normalize-pbxproj.py - Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/ConfigValidation/CmuxConfigSchema.generated.swift: generate-cmux-config-schema.py, regenerated from the merged schema (both sides changed the schema) Catch-up-previous-head: c815fcc Catch-up-base: 0abac32
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 53 minutes. View limit detailsLimit details: You’ve used all 10 included reviews currently available. Review configuration: ⚙️ Run configuration
⛔ Files ignored due to path filters (1)
📒 Files selected for processing (58)
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 |
|
Note Pull Request opener @azooz2003-bit is not an author or co-author of any commit in this PR (commit identities: All contributors have signed the CLA ✍️ ✅ |
Summary
First slice of #15223 (clickable agent actions). With Settings > Terminal > Agent Stop Button on (
agentActions.turnControl, off by default), a small Stop pill sits in a terminal pane's bottom-trailing corner while Claude Code or Codex is running a turn in that pane. Clicking it interrupts the turn.claude_codeorcodexisrunning, the same state the TextBox checks before it forwards Escape. Other agents get no button until their interrupt key is verified. Toggling the setting mid-turn shows or hides the pill at once; it listens for defaults changes only while an agent is running.TerminalPanel.sendNamedKeyResult, the named-key path the TextBox andmobile.chat.interruptalready use. The click is re-checked against the current lifecycle first, so a late click after the turn ended sends nothing.running: Claude Code runs no Stop hook when a turn is interrupted (Sidebar 'Running' status stays on after user interrupts an agent turn #4389), so the pane would keep showing Running and the button would stay up. After sending Escape, cmux asks the agent journal to settle every Claude session its own fold has running on that surface: each gets aturnCompletedevent (AgentJournalEventDraft.userInterrupt, native eventcmux.user_interrupt) with no notification, so no "done" alert fires. Settling from the fold rather than the hook store means nestedclaude -psessions and relayed sessions are handled correctly. If Claude keeps working, its next PreToolUse hook declares the pane running again. Codex is not settled this way: it journals lifecycle only at prompt submit and Stop, so a settle could leave a still-working Codex pane idle.TerminalAgentTurnControlView, an AppKit overlay mounted inGhosttySurfaceScrollViewbeside the password-input badge and raised above the find overlay. Only the button takes clicks; the rest of the overlay passes hit-testing through to the terminal. Workspace and Dock lifecycle writes both refresh it.agentActionssection in the catalog,cmux.json({"agentActions": {"turnControl": true}}), the schema, and settings search. Later categories from the RFC add one key each.Testing
swift test --package-path Packages/macOS/CmuxAgentJournal --filter AgentJournalUserInterruptTests(local, 3 passed): turnStarted, interrupt, turnStarted gives running, idle with no notification identity, then running, forclaudeandcodex; the fold's interrupt drafts cover only live running sessions on the surface (including a session without an id) and settle it to idle without touching another surface.swift test --package-path Packages/macOS/CmuxSettings --filter SettingCatalogTests(local, passed): the key defaults off and matches its section prefix.cmuxTests/TerminalAgentTurnControlTests.swift(added, runs in CI): the pill shows only while enabled and running, hides mid-turn when the setting turns off, stays hidden by default, two quick Stop clicks enqueue exactly one key event, and a click after the agent stopped enqueues none. A journal-center test checks thatrecordUserInterruptjournals the interrupt for the session the center has running on the surface.python3 tests/test_cmux_settings_supported_paths.py(local, passed).python3 scripts/verify-local.py: 15/15 selected checks passed (localization parity, xcstrings, schema, project and test wiring).scripts/ui-test AgentActionPillUITestsat 6c11b3e (run, both tests passed): with the setting on, journaling anagent.turn.startedfor a Claude Code session on a terminal over the socket shows the Stop pill; clicking it hides the pill, so the interrupt settled the turn. With the setting off, the same event shows no pill. The turn is journaled directly, so a live Claude process was not interrupted.Changelog
Added: An optional Stop button over a terminal interrupts a running Claude Code or Codex turn (Settings > Terminal > Agent Stop Button).
Demo Video
Captured by
cmuxUITests/AgentActionPillUITestsin CI run 36425803310 at 6c11b3e (shell prompt blanked). Running, then after clicking Stop:🤖 Generated with Claude Code
Summary by CodeRabbit
Migrated from #15234 after correcting the PR author identity. The head branch and commit history are preserved.
Summary by cubic
Adds an optional Stop button for interrupting running Claude Code or Codex turns, behind a new
agentActions.turnControlsetting (off by default, Settings > Terminal > Agent Stop Button). When enabled, a Stop pill appears in a terminal pane's bottom-trailing corner while a supported agent is running a turn there, and clicking it sends the agent's interrupt key.Behavior
claude_codeorcodexis running; other agents get no button until their interrupt key is verified, and toggling the setting shows or hides it at once.Settings
agentActionscatalog section withturnControl, wired intocmux.json, the config schema, and settings search.Written for commit 8e0de7c. Summary will update on new commits.