From dbb13c2c38d73d7d15fd37e9c8c109efe30aa1fa Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Fri, 6 Nov 2026 01:28:04 -0800 Subject: [PATCH] fix(codex): keep the clipboard SSH marker out of commands Codex runs (#38914) Orca sets a synthetic SSH_CONNECTION so Codex forwards copies over OSC 52 for remote viewers. Codex's shell tool inherited it, so agy launched from Codex treated the local host as remote and ignored its keyring login. The wrapper now blanks only Orca's sentinel in Codex's shell environment policy; genuine SSH metadata is untouched. Fixes #37723 (STA-13596) --- .../shell-startup/codex-shell-no-daemon.test.ts | 17 +++++++++++++++-- src/shared/codex-shell-function.ts | 11 ++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/pty/shell-startup/codex-shell-no-daemon.test.ts b/src/main/pty/shell-startup/codex-shell-no-daemon.test.ts index d82bd72c16b..78a70bbd3e7 100644 --- a/src/main/pty/shell-startup/codex-shell-no-daemon.test.ts +++ b/src/main/pty/shell-startup/codex-shell-no-daemon.test.ts @@ -236,7 +236,15 @@ describe('codex wrapper --no-daemon rule', () => { ['0', undefined, undefined, '', '0'], ['1', '', undefined, '', '0'], ['1', '10.0.0.1 5000 10.0.0.2 22', undefined, '10.0.0.1 5000 10.0.0.2 22', '0'], - ['1', undefined, '/dev/ttys004', '', '0'] + ['1', undefined, '/dev/ttys004', '', '0'], + // Pane-level marker from a remote-viewer Codex launch. + [ + '0', + CODEX_REMOTE_VIEWER_SSH_CONNECTION, + undefined, + CODEX_REMOTE_VIEWER_SSH_CONNECTION, + '0' + ] ].filter(([, inherited]) => !isPowerShell(shell) || inherited !== '') )( 'routes manual copies for remote=%s without marking the parent shell', @@ -253,8 +261,13 @@ describe('codex wrapper --no-daemon rule', () => { ...(inheritedTty ? { SSH_TTY: inheritedTty } : {}) }) expect(result.status, result.stderr).toBe(Number(exitCode)) + // Codex keeps the marker for OSC 52, but blanks it for the commands it runs. + const clearMarker = + expected === CODEX_REMOTE_VIEWER_SSH_CONNECTION + ? "-c|shell_environment_policy.set.SSH_CONNECTION=''|" + : '' expect(lines(result.stdout)).toEqual([ - `ARGV|--no-daemon|clipboard|ssh=${expected}`, + `ARGV|--no-daemon|${clearMarker}clipboard|ssh=${expected}`, `ARGV|shell|ssh=${inherited ?? ''}` ]) } diff --git a/src/shared/codex-shell-function.ts b/src/shared/codex-shell-function.ts index dc344821405..c217b2d2109 100644 --- a/src/shared/codex-shell-function.ts +++ b/src/shared/codex-shell-function.ts @@ -19,6 +19,10 @@ export const CODEX_COMMAND_IDENTITY_ENV_KEYS = [ 'ORCA_USER_DATA_PATH' ] as const +// Blank Orca's clipboard marker for commands Codex runs: agy reads SSH_CONNECTION as a remote host and ignores its local login (#37723). +// Why set, not exclude: -c exclude would replace the user's own exclude list. Real SSH never reports port 0. +const CLEAR_SSH_MARKER_ARG = "shell_environment_policy.set.SSH_CONNECTION=''" + // Skip apostrophes: TOML literals cannot hold them and Codex parse fallback may truncate paths; missing identity is safer than wrong identity. const identitySetArg = (key: string, value: string): string => `shell_environment_policy.set.${key}='${value}'` @@ -50,6 +54,7 @@ if [[ -n "\${__orca_codex_binary:-}" && -x "\${__orca_codex_binary}" ]]; then done if [[ "$__orca_codex_identity" != 0 ]]; then ${CODEX_COMMAND_IDENTITY_ENV_KEYS.map((key) => ` [[ -z "\${${key}:-}" || "\${${key}}" == *"'"* ]] || set -- -c "${identitySetArg(key, `\${${key}}`)}" "$@"`).join('\n')} + [[ "\${SSH_CONNECTION:-}" != '${CODEX_REMOTE_VIEWER_SSH_CONNECTION}' ]] || set -- -c "${CLEAR_SSH_MARKER_ARG}" "$@" fi # Why probe every launch: a cached answer goes stale across an upgrade, and 0.155 and older exit 2 on the flag. if [[ "$__orca_codex_isolate" != 0 ]]; then @@ -74,6 +79,9 @@ if test "$__orca_codex_type" = file end if test "$ORCA_CODEX_ISOLATE" != 0; and not string match -qr -- '${CODEX_SHARED_SERVER_ARG_PATTERN}' $argv ${CODEX_COMMAND_IDENTITY_ENV_KEYS.map((key) => ` test -n "$${key}"; and not string match -q -- "*'*" "$${key}"; and set argv -c "${identitySetArg(key, `$${key}`)}" $argv`).join('\n')} + if test "$SSH_CONNECTION" = '${CODEX_REMOTE_VIEWER_SSH_CONNECTION}'; or begin; test "$${REMOTE_VIEWER_CLIPBOARD_ENV}" = 1; and not set -q SSH_CONNECTION; and not set -q SSH_TTY; end + set argv -c "${CLEAR_SSH_MARKER_ARG}" $argv + end end if test "$ORCA_CODEX_ISOLATE" != 0; and not string match -qr -- '${CODEX_NO_DAEMON_SKIP_ARG_PATTERN}' $argv; and command codex --help 2>/dev/null `'${key}'`).join(',')}) { $v = [Environment]::GetEnvironmentVariable($k); if ($v -and -not $v.Contains("'")) { $orcaCodexIdentity += '-c', "${identitySetArg('$k', '$v')}" } } + if ($orcaCodexRemoteClipboard -or $env:SSH_CONNECTION -ceq '${CODEX_REMOTE_VIEWER_SSH_CONNECTION}') { $orcaCodexIdentity += '-c', "${CLEAR_SSH_MARKER_ARG}" } } $orcaCodexArgs = @(__OrcaEscapeNativeArgs $orcaCodexExecutable.Source (@($orcaCodexIdentity) + @($args))) - $orcaCodexRemoteClipboard = $env:${REMOTE_VIEWER_CLIPBOARD_ENV} -eq '1' -and -not (Test-Path Env:SSH_CONNECTION) -and -not (Test-Path Env:SSH_TTY) if ($orcaCodexRemoteClipboard) { $env:SSH_CONNECTION = '${CODEX_REMOTE_VIEWER_SSH_CONNECTION}' } # Why: a native command inside a function never sees the function's pipeline input on its own. try {