Repository navigation
Add opt-in setting to paste clipboard images into agents with Ctrl+V - #17711
azooz2003-bit wants to merge 8 commits into
Conversation
Cmd+V with an image on the clipboard saves it to a temporary file and pastes the path. Claude Code and Codex can attach an image straight from the clipboard when they receive Ctrl+V, which is what users of those agents expect in other terminals. `terminal.agentImagePasteSendsCtrlV` (off by default) makes Cmd+V send Ctrl+V instead, but only when all of these hold; otherwise the existing paste runs unchanged: - Claude Code or Codex is live in the pane according to its hooks (`agentPIDKey`), not merely launched or restored earlier. - The clipboard advertises image data and no text, rich text, URL or file type. Only the type list is read on the main thread. - The pane is local. cmux ssh, detected SSH and Cloud panes keep the upload path, since the agent there reads another machine's clipboard. - The panel is not hibernated and the key is delivered. The setting is wired through the CmuxSettings catalog, cmux.json schema, Settings > Terminal row, search index and all-keys reference, with strings in all nine app locales. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The first version trusted the pane's recorded agent PIDs, which can be stale or belong to an agent that isn't reading the keyboard, so a shell or tmux could receive a stray ^V. - Prune stale PIDs with `clearStaleAgentPIDs(panelId:)` before reading the agent context, as the TextBox submit-action cycle does. An agent killed without an exit hook no longer counts. - Require one of the pane's recorded Claude Code or Codex PIDs to be in the terminal's foreground process group (`foregroundProcessID()`, which is Ghostty's tcgetpgrp). A Ctrl+Z-suspended agent, or one in another tmux window, keeps the temp-path paste. - The workspace-level decision moves into `TerminalAgentImagePasteRouting` with the foreground group and getpgid injected, so both cases are tested against a real Workspace. The setting check still runs first. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…gents # Conflicts: # Packages/macOS/CmuxFoundation/Sources/CmuxFoundation/ConfigValidation/CmuxConfigSchema.generated.swift # cmux.xcodeproj/project.pbxproj
Catch-up merge by scripts/ci/catch_up_pr.py (RFC #14631). Merged by scripts/merge-main.sh: origin/main at 478e323. 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: ea84ae6 Catch-up-base: 478e323
Catch-up merge by scripts/ci/catch_up_pr.py (RFC #14631). Merged by scripts/merge-main.sh: origin/main at 1b06f84. Resolved conflicts: - Resources/Localizable.xcstrings: xcstrings key-level union - cmux.xcodeproj/project.pbxproj: union of added entries, then normalize-pbxproj.py Catch-up-previous-head: cb9613a Catch-up-base: 1b06f84
Resolved the settings reference conflict by keeping both sides. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Catch-up merge by scripts/ci/catch_up_pr.py (RFC #14631). Merged by scripts/merge-main.sh: origin/main at 5eda931. Resolved conflicts: - Resources/Localizable.xcstrings: xcstrings key-level union - cmux.xcodeproj/project.pbxproj: union of added entries, then normalize-pbxproj.py Catch-up-previous-head: b0d1f5d Catch-up-base: 5eda931 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Catch-up merge by scripts/ci/catch_up_pr.py (RFC #14631). Merged by scripts/merge-main.sh: origin/main at ea6e02b. 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: 54bdee8 Catch-up-base: ea6e02b
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
|
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 54 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 (37)
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
With an image on the clipboard, Cmd+V in a terminal saves the image to a temporary file and pastes its path. Claude Code and Codex can attach an image straight from the macOS clipboard when they receive Ctrl+V, the way they work in other terminals, but cmux never sends it.
This adds
terminal.agentImagePasteSendsCtrlV, off by default, with a row in Settings > Terminal ("Paste Images into Agents with Ctrl+V"). When it's on, Cmd+V (and Edit > Paste) sends Ctrl+V to the pane instead of pasting a path, but only when all of these hold. Anything else runs the existing paste unchanged:WorkspaceContentView.terminalAgentContext+TextBoxAgentDetection) and requires the hook-reportedagentPIDKey. A launch command or restored snapshot alone doesn't count, because Ctrl+V sent to a shell after the agent exits would drop the image.TextBoxAgentDetectiongains a per-agentmatchesActive(context:), next to the existingsupportsActiveAgentPrefixes.Workspace.clearStaleAgentPIDs(panelId:)(as the TextBox submit-action cycle does), so an agent killed without an exit hook no longer counts. Then one of the pane's recorded Claude/Codex PIDs must be in the terminal's foreground process group (TerminalSurface.foregroundProcessID(), which is Ghostty'stcgetpgrp). A Ctrl+Z-suspended agent, or one in another tmux window, keeps the temp-path paste instead of sending ^V to the shell or tmux. Limit: a child the agent runs in its own process group, such as its external editor, shares that group and still counts.resolvedImageTransferTarget == .local). For cmux ssh, detected SSH and Cloud panes the agent would read another machine's clipboard, so they keep the upload path.sendNamedKey("ctrl+v")fails, the regular paste runs.Ctrl+V goes through
TerminalPanel.sendNamedKey, so Ghostty encodes it for the pane's active keyboard mode like any typed key. The checks run in order of cost: the setting first, then agent context, then the pasteboard type list, then target resolution. With the setting off,paste(_:)does oneUserDefaultsread before its existing code.Wiring: CmuxSettings catalog key, cmux.json section mapping and supported paths,
web/data/cmux.schema.json(plus the regenerated embeddedCmuxConfigSchema.generated.swift) withdescriptionKey: schemaDescriptions.terminal.agentImagePasteSendsCtrlV, Settings row, curated and app search index entries, andskills/cmux-settings/references/all-keys.md.Related but separate: #11314 (Cmd+C/Cmd+V routing), #7046 and #5586 (remote image upload paths). This PR changes neither routing nor uploads.
Testing
cmuxTests/TerminalAgentImagePasteRoutingTests.swift(wired with./scripts/sync-test-wiring). It checks the decision matrix: setting on or off (off never evaluates the agent, pasteboard or target), live Claude/Codex vs other or exited agents, image-only vs image plus each text, URL or file type, and local vs remote or Cloud targets.stalePIDWithoutAnExitHookKeepsTheTempPathPaste,liveAgentOutsideTheForegroundGroupKeepsTheTempPathPasteandliveForegroundAgentInAWorkspaceSendsCtrlVrun the workspace-level decision against a realWorkspacewith recorded PIDs.foregroundCheckMatchesTheAgentsProcessGroup,agentOutsideTheForegroundKeepsTheTempPathPasteandonlyClaudeCodeAndCodexPIDsCountForTheForegroundCheckcover the injected foreground predicate.SettingCatalogTests.agentImagePasteSendsCtrlVDefaultsOffand the new row path toSettingsRowAnchorResolutionTests.python3 scripts/verify-local.py(swift-syntax, xcstrings, localization, project, config-schema, test-wiring, package-groups, feature-flags: 8/8 pass), plustests/test_cmux_schema_parity.pyandtests/test_cmux_settings_supported_paths.py. CI on bd8600e (rebased onto Add terminal.confirmUnsafePaste to confirm unsafe pastes in a window sheet #14951) is green: the app compiled, all ofAgent image paste routing(including the six stale-PID and foreground tests above) ran and passed inapp-host unit tests (changed suites), andagentImagePasteSendsCtrlVDefaultsOffpassed inswift-package-tests.claudeorcodex(the image should attach) and in a plain shell pane (a temp path should paste, as before).Localization audit: four new keys in
Resources/Localizable.xcstrings(the row title, the on and off subtitles, and the search alias), each with all nine app locales.scripts/localization_catalog.py checkreports 0 parity errors andscripts/lint-xcstrings.pypasses. The schema description has adescriptionKeywith a translated entry in all 20 web locales (web/messages/*.json), added in ea84ae6 after review. The Korean subtitle uses 분할 창 for panes.Checklist
— Glasswren g1 🎲
run: run_paste_improvements_20260927_d07ca05d
🤖 Generated with Claude Code
Summary by CodeRabbit
Migrated from #14948 after correcting the PR author identity. The head branch and commit history are preserved.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Adds an opt-in
terminal.agentImagePasteSendsCtrlVsetting (off by default) that makes Cmd+V send Ctrl+V to a live Claude Code or Codex in a local pane when the clipboard holds only image data, so the agent attaches the image directly from the clipboard. All other cases keep the existing behavior: saving the image to a temporary file and pasting or uploading its path.The setting takes over only when every condition holds; otherwise the regular paste runs unchanged:
cmux ssh, detected SSH, and Cloud panes keep the upload path.Also includes the Settings > Terminal row, cmux.json schema entry, search index entries, settings reference, and localization in all supported locales.
Written for commit 9754f85. Summary will update on new commits.