chore: post-merge cleanup for agent integration fixes (#33644)

Post-merge cleanup after the agent-integration fixes: remove the unreachable Windows hook-script stdin drain epilogue; Windows statusline exits instead of draining outside an Orca pane; Codex CLI fork uses the \\?\-aware rollout path; WSL browser-network relay falls back to orcad's pinned Node (shared launcher revision stamp); stranded sleep records only drop for their own worktree; renames (hidden probe cwd, local Windows ConPTY helper).
This commit is contained in:
Neil
2026-10-25 15:30:00 -07:00
committed by GitHub
parent f53e7b016a
commit c6769fc9be
37 changed files with 161 additions and 137 deletions
+1 -1
View File
@@ -9094,7 +9094,7 @@
"src/renderer/src/components/terminal-pane/terminal-context-menu-dismiss.test.ts": 33,
"src/renderer/src/components/terminal-pane/terminal-copy-rejection-handling.test.ts": 44,
"src/renderer/src/components/terminal-pane/terminal-copy-shortcut.test.ts": 1457,
"src/renderer/src/components/terminal-pane/terminal-ctrl-arrow-conpty.test.ts": 1372,
"src/renderer/src/components/terminal-pane/terminal-pane-local-windows-conpty.test.ts": 1372,
"src/renderer/src/components/terminal-pane/terminal-ctrl-enter.test.ts": 92,
"src/renderer/src/components/terminal-pane/terminal-cursor-appearance-precedence.test.ts": 296,
"src/renderer/src/components/terminal-pane/terminal-cursor-inactive-style.test.ts": 337,
@@ -132,7 +132,6 @@ export function buildPosixHookSpoolLines(source: string, eventNameVar?: string):
]
}
export const WINDOWS_HOOK_STDIN_DRAIN_LABEL = 'orca_agent_hook_drain_stdin'
// Windows searches the worktree before PATH; never give hook payloads to a repo-local executable.
export const WINDOWS_HOOK_STDIN_READER = '"%SystemRoot%\\System32\\more.com"'
export const WINDOWS_HOOK_STDIN_DRAIN_COMMAND = `${WINDOWS_HOOK_STDIN_READER} >nul 2>nul`
@@ -157,7 +156,3 @@ export const WINDOWS_GIT_BASH_HOOK_ENVIRONMENT_GUARD = `if ${WINDOWS_HOOK_ENVIRO
export const WINDOWS_POWERSHELL_HOOK_ENVIRONMENT_GUARD = `if (${WINDOWS_HOOK_ENVIRONMENT_VARS.map(
(name) => `-not $env:${name}`
).join(' -or ')}) { exit 0 }`
export function buildWindowsHookStdinDrainEpilogue(): string[] {
return [`:${WINDOWS_HOOK_STDIN_DRAIN_LABEL}`, WINDOWS_HOOK_STDIN_DRAIN_COMMAND, 'exit /b 0']
}
@@ -412,7 +412,7 @@ describe('Windows managed hook stdin structure', () => {
: [scriptPath]
const result = await runHookProcess(executable, args, hookEnvironment())
expect(result.exitCode, `${fileName} exit code`).toBe(0)
// Only missing-context Windows paths permit broken writers; in-pane hooks must drain.
// Missing-context guards exit before reading stdin, so writers may see a broken pipe.
for (const error of result.stdinErrors) {
expect(WRITER_BROKEN_BY_EARLY_EXIT, `${fileName} stdin error`).toContain(error.code)
}
+3 -13
View File
@@ -1,8 +1,8 @@
import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'
import { createHash } from 'node:crypto'
import { existsSync, readFileSync } from 'node:fs'
import { join } from 'node:path'
import { relayBundleCandidates } from '../ssh/relay-bundle-paths'
import { withWslGuestLauncherRevision } from '../wsl/wsl-guest-launcher-revision'
import { orcadPosixNodeRuntimeGlobs } from '../managed-server/install/orcad-node-runtime-globs'
import type { MultiplexerTransport } from '../ssh/ssh-channel-multiplexer'
@@ -36,7 +36,7 @@ export function resolveWslHookRelayBundle(): WslHookRelayBundle | null {
const version = readFileSync(versionPath, 'utf8').trim()
// Versions become shell literals and path segments; accept only a safe alphabet.
if (/^[A-Za-z0-9+.-]+$/.test(version)) {
return { jsPath, version: `${version}.${guestLauncherRevision()}` }
return { jsPath, version: withWslGuestLauncherRevision(version, buildGuestLaunchScript) }
}
}
}
@@ -48,20 +48,10 @@ function guestRelayDirExpr(version: string): string {
return `$HOME/${WSL_HOOK_RELAY_DIR}/${version}`
}
let launcherRevision: string | undefined
// The bundle hash alone misses launcher edits; a new guest dir forces one reinstall of launch.sh.
function guestLauncherRevision(): string {
launcherRevision ??= createHash('sha256')
.update(buildGuestLaunchScript('0'))
.digest('hex')
.slice(0, 8)
return launcherRevision
}
/** Probe candidate Node versions: noninteractive sh may miss a newer nvm installation. */
export function buildGuestLaunchScript(version: string): string {
const dir = guestRelayDirExpr(version)
// A WSL host distro may have no system node but always has orcad's pinned runtime.
// A WSL host distro may have no system node but usually has orcad's pinned runtime.
const [orcadRuntime, orcadMachineRuntimes] = orcadPosixNodeRuntimeGlobs()
return [
'#!/bin/sh',
@@ -8,6 +8,7 @@ import type { AgentJournalSnapshot } from '../../shared/agent-session-journal-ty
import { agentJournalSubmissionKey } from '../../shared/agent-session-journal-item-key'
import { setStructuredAgentSessionHost } from '../native-chat/agent-session-wire/structured-agent-session-registry'
import { prepareStructuredChatCliFork } from './structured-chat-cli-fork'
import { structuredAgentRuntimeRegistration } from '../runtime/structured-agent-runtime-registrations'
import { prepareLegacySharedCodexSessionResume } from '../codex/codex-legacy-session-resume'
import { AiVaultPrepareSessionResumeParams } from '../../shared/rpc-contract/ai-vault-params'
@@ -98,6 +99,43 @@ it('locates the current Codex rollout in its recorded home and materializes the
})
})
it.runIf(process.platform === 'win32')(
'maps an extended-length Windows rollout path into the prepared home (#27200)',
async () => {
root = await mkdtemp(join(tmpdir(), 'chat-cli-fork-'))
vi.stubEnv('ORCA_USER_DATA_PATH', root)
const home = join(root, 'codex-runtime-home', 'home')
const system = join(root, 'system')
const record = agentSessionRecordFixture()
record.provider = 'codex'
record.accountHome = { variable: 'CODEX_HOME', path: home }
record.providerHandleChain = [
{
...record.providerHandleChain[0]!,
handle: { transport: 'codex-app-server', agent: 'codex', nativeId: 'current-conversation' }
}
]
const relative = join('2026', '10', '15', 'rollout-2026-10-15-current-conversation.jsonl')
await mkdir(join(home, 'sessions', '2026', '10', '15'), { recursive: true })
await writeFile(join(home, 'sessions', relative), '{"saved":true}\n')
vi.spyOn(
structuredAgentRuntimeRegistration('codex')!.transcriptImport!,
'findTranscript'
).mockResolvedValueOnce(`\\\\?\\${join(home, 'sessions', relative)}`)
install(record)
const result = await prepareStructuredChatCliFork(request, async (args) => ({
...(await prepareLegacySharedCodexSessionResume(args, {
isHostSystemDefaultRealHomeSelected: () => true,
legacyCodexHomePath: home,
systemCodexHomePath: system
})),
useRealCodexHome: false,
substituteCodexHome: system
}))
expect(result.cliForkSource?.filePath).toBe(join(system, 'sessions', relative))
}
)
it('copies OpenCode directly from the selected record and preserves its typed account and copy id', async () => {
root = await mkdtemp(join(tmpdir(), 'chat-cli-fork-'))
const record = agentSessionRecordFixture()
@@ -1,6 +1,7 @@
import { stat } from 'node:fs/promises'
import { isAbsolute, join, relative } from 'node:path'
import { isAbsolute, join } from 'node:path'
import { getSystemCodexHomePath } from '../codex/codex-home-paths'
import { codexDatedRolloutRelativePath } from '../codex/codex-session-resume-home'
import { agentForksConversations, agentForksThroughChat } from '../../shared/agent-resume-argv'
import { isLegacyAgentSessionAccountHome } from '../../shared/agent-session-account-home'
import { agentSessionProviderHandleChainHead } from '../../shared/agent-session-provider-handle'
@@ -173,11 +174,12 @@ export async function prepareStructuredChatCliFork(
})
const preparedHome = result.useRealCodexHome ? null : (result.substituteCodexHome ?? codexHome)
if (codexHome && (result.useRealCodexHome || result.substituteCodexHome)) {
filePath = join(
preparedHome ?? getSystemCodexHomePath(),
'sessions',
relative(join(codexHome, 'sessions'), filePath)
)
// Codex canonicalizes CODEX_HOME, so Windows transcripts can carry a \\?\ prefix (#27200).
const relativePath = codexDatedRolloutRelativePath(join(codexHome, 'sessions'), filePath)
if (!relativePath) {
throw new Error('The prepared conversation is unavailable in the selected account.')
}
filePath = join(preparedHome ?? getSystemCodexHomePath(), 'sessions', relativePath)
const preparedFile = await stat(filePath)
if (!preparedFile.isFile() || preparedFile.size === 0) {
throw new Error('The prepared conversation is unavailable in the selected account.')
@@ -1,16 +1,19 @@
import { execFileSync } from 'node:child_process'
import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { orcadPosixNodeRuntimeGlobs } from '../managed-server/install/orcad-node-runtime-globs'
import {
buildWslBrowserNetworkGuestInstallScript,
buildWslBrowserNetworkGuestLaunchScript
buildWslBrowserNetworkGuestLaunchScript,
resolveWslBrowserNetworkRelayBundle
} from './wsl-browser-network-relay-launch'
const roots: string[] = []
afterEach(() => {
vi.unstubAllEnvs()
for (const root of roots.splice(0)) {
rmSync(root, { recursive: true, force: true })
}
@@ -29,6 +32,22 @@ describe('WSL browser network relay launch', () => {
)
})
it('falls back to orcad pinned runtimes after system node, under a launcher-stamped version', () => {
const forLine = buildWslBrowserNetworkGuestLaunchScript('0.1.0+abc')
.split('\n')
.find((line) => line.startsWith('for c in '))
const [orcadRuntime, orcadMachineRuntimes] = orcadPosixNodeRuntimeGlobs()
expect(forLine).toContain(`"$HOME/.local/bin/node" ${orcadRuntime} ${orcadMachineRuntimes}; do`)
const relayRoot = mkdtempSync(join(tmpdir(), 'orca-wsl-browser-network-bundle-'))
roots.push(relayRoot)
vi.stubEnv('ORCA_RELAY_PATH', relayRoot)
mkdirSync(join(relayRoot, 'wsl'))
writeFileSync(join(relayRoot, 'wsl', 'wsl-browser-network-relay.js'), '')
writeFileSync(join(relayRoot, 'wsl', '.browser-network-version'), '0.1.0+abc\n')
expect(resolveWslBrowserNetworkRelayBundle()?.version).toMatch(/^0\.1\.0\+abc\.[0-9a-f]{8}$/)
})
it('installs exact bundle bytes and writes the version marker last', () => {
const root = mkdtempSync(join(tmpdir(), 'orca-wsl-browser-network-'))
roots.push(root)
@@ -2,6 +2,8 @@ import { spawnProcess } from '@orca/process-host'
import { existsSync, readFileSync } from 'node:fs'
import { join } from 'node:path'
import { relayBundleCandidates } from '../ssh/relay-bundle-paths'
import { withWslGuestLauncherRevision } from '../wsl/wsl-guest-launcher-revision'
import { orcadPosixNodeRuntimeGlobs } from '../managed-server/install/orcad-node-runtime-globs'
import {
WSL_BROWSER_NETWORK_RELAY_BUNDLE_NAME,
WSL_BROWSER_NETWORK_RELAY_DIR,
@@ -33,7 +35,10 @@ export function resolveWslBrowserNetworkRelayBundle(): WslBrowserNetworkRelayBun
}
const version = readFileSync(versionPath, 'utf8').trim()
if (/^[A-Za-z0-9+.-]+$/.test(version)) {
return { jsPath, version }
return {
jsPath,
version: withWslGuestLauncherRevision(version, buildWslBrowserNetworkGuestLaunchScript)
}
}
}
return null
@@ -48,13 +53,15 @@ function guestRelayDir(version: string): string {
export function buildWslBrowserNetworkGuestLaunchScript(version: string): string {
const dir = guestRelayDir(version)
// A WSL host distro may have no system node but usually has orcad's pinned runtime.
const [orcadRuntime, orcadMachineRuntimes] = orcadPosixNodeRuntimeGlobs()
return [
'#!/bin/sh',
`d="${dir}"`,
`v="$(cat "$d/${WSL_BROWSER_NETWORK_RELAY_VERSION_FILE}" 2>/dev/null || true)"`,
`[ "$v" = '${version}' ] || exit ${WSL_BROWSER_NETWORK_RELAY_STALE_EXIT_CODE}`,
'n=""',
'for c in "$(command -v node 2>/dev/null || true)" "$HOME/.nvm/versions/node"/*/bin/node /usr/local/bin/node /usr/bin/node "$HOME/.local/bin/node"; do',
`for c in "$(command -v node 2>/dev/null || true)" "$HOME/.nvm/versions/node"/*/bin/node /usr/local/bin/node /usr/bin/node "$HOME/.local/bin/node" ${orcadRuntime} ${orcadMachineRuntimes}; do`,
' [ -n "$c" ] && [ -x "$c" ] || continue',
` if "$c" -e 'process.exit(Number(process.versions.node.split(".")[0])>=18?0:1)' 2>/dev/null; then`,
' n="$c"',
+1 -3
View File
@@ -17,8 +17,7 @@ import {
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
// Daemon workers inherit stale pane keys; exit before draining their potentially abandoned stdin.
@@ -58,7 +57,6 @@ export function getManagedScript(
// Avoid a PowerShell startup per hook.
buildWindowsAgentHookCurlPostCommand(source),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -3
View File
@@ -18,7 +18,6 @@ vi.mock('../git-bash', async (importOriginal) => ({
import type { SFTPWrapper } from 'ssh2'
import { createManagedCommandMatcher, WINDOWS_CMD_SAFE_PATH } from '../agent-hooks/installer-utils'
import { WINDOWS_HOOK_STDIN_DRAIN_LABEL } from '../agent-hooks/hook-stdin-contract'
import { ClaudeHookService } from './hook-service'
import { CLAUDE_EVENTS } from './claude-managed-hook-events'
import { getWindowsManagedLifecycleHook, OPENCLAUDE_HOOK_SETTINGS } from './hook-settings'
@@ -656,7 +655,7 @@ describe('backgrounded-session pane guard (#9236)', () => {
expect(guard).toBeDefined()
expect(script.indexOf('CLAUDE_JOB_DIR')).toBeLessThan(script.indexOf('curl'))
if (target === 'win32') {
expect(guard).not.toContain(WINDOWS_HOOK_STDIN_DRAIN_LABEL)
expect(guard).toBe('if not "%CLAUDE_JOB_DIR%"=="" exit /b 0')
}
} finally {
Object.defineProperty(process, 'platform', platform)
@@ -680,7 +679,6 @@ describe('backgrounded-session pane guard (#9236)', () => {
)
const guard = script.split('\r\n').find((line) => line.includes('CLAUDE_JOB_DIR'))
expect(guard).toBe('if not "%CLAUDE_JOB_DIR%"=="" exit /b 0')
expect(guard).not.toContain(WINDOWS_HOOK_STDIN_DRAIN_LABEL)
} finally {
Object.defineProperty(process, 'platform', platform)
vi.unstubAllEnvs()
+4 -7
View File
@@ -82,16 +82,13 @@ describe('getManagedStatusLineScript (win32 local)', () => {
expect(script).not.toContain('"configDir=%CLAUDE_CONFIG_DIR%"')
})
it('drains stdin before exiting when the pane key is missing', () => {
it('exits without reading stdin when the pane key is missing (#24907)', () => {
stubPlatform('win32')
const script = getManagedStatusLineScript('local')
const paneGuardIndex = script.indexOf(
'if "%ORCA_PANE_KEY%"=="" goto :orca_agent_hook_drain_stdin'
)
const captureIndex = script.indexOf('more.com')
const paneGuardIndex = script.indexOf('if "%ORCA_PANE_KEY%"=="" exit /b 0')
expect(paneGuardIndex).toBeGreaterThan(-1)
expect(paneGuardIndex).toBeLessThan(captureIndex)
expect(script).toContain(':orca_agent_hook_drain_stdin')
expect(paneGuardIndex).toBeLessThan(script.indexOf('more.com'))
expect(script.match(/more\.com/g)).toHaveLength(1)
})
it('throttles with an all-builtin seconds-of-day stamp that fails open to posting', () => {
+2 -7
View File
@@ -1,9 +1,5 @@
import { posixCurlCommand } from '../agent-hooks/hook-post-command'
import {
buildWindowsHookStdinDrainEpilogue,
WINDOWS_HOOK_STDIN_DRAIN_LABEL,
WINDOWS_HOOK_STDIN_READER
} from '../agent-hooks/hook-stdin-contract'
import { WINDOWS_HOOK_STDIN_READER } from '../agent-hooks/hook-stdin-contract'
import {
CLAUDE_STATUSLINE_MIN_POST_INTERVAL_SECONDS,
CLAUDE_STATUSLINE_PATHNAME
@@ -31,7 +27,7 @@ export function getManagedStatusLineScript(target: 'local' | 'posix' = 'local'):
// Daemon workers inherit stale pane keys; exit before reading their potentially abandoned stdin.
'if not "%CLAUDE_JOB_DIR%"=="" exit /b 0',
// Endpoint refresh never sets the pane key, so this guard can precede stdin capture.
`if "%ORCA_PANE_KEY%"=="" goto :${WINDOWS_HOOK_STDIN_DRAIN_LABEL}`,
'if "%ORCA_PANE_KEY%"=="" exit /b 0',
// UUID suffixes and sanitized legacy delimiters keep filenames safe.
'set "ORCA_STATUSLINE_PANE_ID=%ORCA_PANE_KEY:~-36%"',
'set "ORCA_STATUSLINE_PANE_ID=%ORCA_STATUSLINE_PANE_ID::=_%"',
@@ -80,7 +76,6 @@ export function getManagedStatusLineScript(target: 'local' | 'posix' = 'local'):
`:${STATUSLINE_CLEANUP_LABEL}`,
'del "%ORCA_STATUSLINE_PAYLOAD_FILE%" >nul 2>nul',
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+3 -2
View File
@@ -11,7 +11,7 @@ import {
} from '../agent-hooks/managed-hook-script-refresh'
import {
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
WINDOWS_HOOK_STDIN_DRAIN_COMMAND
} from '../agent-hooks/hook-stdin-contract'
import {
resolveBundledWindowsNativeHookExecutable,
@@ -85,7 +85,8 @@ export function getWindowsClaudeHookEntry(): string {
'echo {}',
...buildWindowsHookEnvironmentGuardLines(),
WINDOWS_CLAUDE_BACKGROUND_JOB_GUARD,
...buildWindowsHookStdinDrainEpilogue(),
WINDOWS_HOOK_STDIN_DRAIN_COMMAND,
'exit /b 0',
''
].join('\r\n')
}
+2 -2
View File
@@ -16,7 +16,7 @@ import {
import { runCodexAppServerSession } from './codex-app-server-session'
import { isCodexSkillsListUnsupported, requestCodexSkillReport } from './codex-skill-list'
import { supportsCodexStructuredLocation } from './codex-structured-location-support'
import { resolveHiddenRateLimitPtyCwd } from '../rate-limits/hidden-rate-limit-pty-cwd'
import { resolveHiddenProbeCwd } from '../rate-limits/hidden-probe-cwd'
import {
resolveCodexStructuredInvocation,
type CodexStructuredInvocationDeps
@@ -108,7 +108,7 @@ async function probeCodexSkills(
command,
args: [...args, 'app-server'],
// skills/list takes the folder per request; a settling probe in it blocks Windows worktree removal (#33174).
cwd: resolveHiddenRateLimitPtyCwd(),
cwd: resolveHiddenProbeCwd(),
cliPath: command,
env: { ...definedEnv(environment), CODEX_HOME: accountHomePath },
envToDelete: [NATIVE_CHAT_SCRATCH_DIR_ENV, LEGACY_NATIVE_CHAT_VISUALS_DIR_ENV],
+1 -3
View File
@@ -2,8 +2,7 @@ import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
import { buildWindowsAgentHookCurlPostCommand } from '../agent-hooks/installer-utils'
@@ -17,7 +16,6 @@ export function getManagedScript(target: 'local' | 'posix' = 'local'): string {
...buildWindowsHookEnvironmentGuardLines(),
buildWindowsAgentHookCurlPostCommand('codex'),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
@@ -3,8 +3,7 @@ import { buildWindowsAgentHookPostCommand } from '../agent-hooks/installer-utils
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
export type CommandCodeManagedScriptTarget = 'local' | 'posix'
@@ -36,7 +35,6 @@ export function buildCommandCodeManagedScript(
'if not "%ORCA_AGENT_HOOK_TOKEN%"=="" exit /b 0',
'for /f "tokens=2 delims==" %%P in (\'findstr /b /c:"set ORCA_AGENT_HOOK_PORT=" "%~1" 2^>nul\') do if "%%P"=="%ORCA_AGENT_HOOK_PORT%" call "%~1" 2>nul',
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -3
View File
@@ -6,8 +6,7 @@ import {
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
export function getManagedScriptFileName(): string {
@@ -34,7 +33,6 @@ export function getManagedScript(target: 'local' | 'posix' = 'local'): string {
' --data-urlencode "hookEventName=%ORCA_COPILOT_HOOK_EVENT%" ^'
]),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -3
View File
@@ -8,8 +8,7 @@ import { posixCurlCommand } from '../agent-hooks/hook-post-command'
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
import {
buildPosixGrokReplayGuardLines,
@@ -59,7 +58,6 @@ export function getManagedScript(target: 'local' | 'posix' = 'local'): string {
...buildWindowsGrokReplayGuardLines(),
buildWindowsAgentHookPostCommand('cursor'),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -3
View File
@@ -14,8 +14,7 @@ import {
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
import { posixCurlCommand } from '../agent-hooks/hook-post-command'
import {
@@ -49,7 +48,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
...buildWindowsHookEnvironmentGuardLines(),
buildWindowsAgentHookPostCommand('devin'),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -3
View File
@@ -25,8 +25,7 @@ import {
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
import { posixCurlCommand } from '../agent-hooks/hook-post-command'
@@ -81,7 +80,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
...buildWindowsHookEnvironmentGuardLines(),
buildWindowsAgentHookPostCommand('droid'),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -3
View File
@@ -18,8 +18,7 @@ import {
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command'
import {
@@ -57,7 +56,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
...buildWindowsHookEnvironmentGuardLines(),
buildWindowsAgentHookCurlPostCommand('dsh'),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -3
View File
@@ -25,8 +25,7 @@ import {
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
import { posixCurlCommand } from '../agent-hooks/hook-post-command'
@@ -64,7 +63,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
...buildWindowsHookEnvironmentGuardLines(),
buildWindowsAgentHookPostCommand('gemini'),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -5
View File
@@ -1,8 +1,5 @@
import { buildWindowsAgentHookPostCommand } from '../agent-hooks/installer-utils'
import {
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
} from '../agent-hooks/hook-stdin-contract'
import { buildWindowsHookEnvironmentGuardLines } from '../agent-hooks/hook-stdin-contract'
/** Matches the envelope length cap used by the POSIX grok-hook branch. */
export const GROK_HOME_ENVELOPE_MAX_LENGTH = 4096
@@ -31,7 +28,6 @@ export function buildWindowsGrokHookScript(): string {
':orca_grok_home_ready',
WINDOWS_GROK_HOOK_POST_COMMAND,
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
-2
View File
@@ -16,7 +16,6 @@ import {
} from '../agent-hooks/installer-utils-remote'
import {
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue,
POSIX_HOOK_STDIN_DRAIN_COMMAND,
WINDOWS_HOOK_STDIN_DRAIN_COMMAND
} from '../agent-hooks/hook-stdin-contract'
@@ -60,7 +59,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
])}`,
`del "${payloadFile}" 2>nul`,
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -3
View File
@@ -32,8 +32,7 @@ import {
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
import { posixCurlCommand } from '../agent-hooks/hook-post-command'
import {
@@ -82,7 +81,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
...buildWindowsHookEnvironmentGuardLines(),
buildWindowsAgentHookCurlPostCommand('kimi'),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -3
View File
@@ -20,8 +20,7 @@ import { refreshManagedScriptIfPresent } from '../agent-hooks/managed-hook-scrip
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command'
import {
@@ -50,7 +49,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
...buildWindowsHookEnvironmentGuardLines(),
buildWindowsAgentHookCurlPostCommand('kiro'),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+1 -3
View File
@@ -17,8 +17,7 @@ import {
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command'
import {
@@ -51,7 +50,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
...buildWindowsHookEnvironmentGuardLines(),
buildWindowsAgentHookCurlPostCommand('muse'),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
+3 -6
View File
@@ -31,10 +31,7 @@ import {
supplementCodexRateLimitResetCredits
} from './codex-reset-credit-client'
import { readCodexRateLimitsViaRpc } from './codex-rpc-rate-limit-probe'
import {
getHiddenRateLimitWslCwdSetupCommands,
resolveHiddenRateLimitPtyCwd
} from './hidden-rate-limit-pty-cwd'
import { getHiddenProbeWslCwdSetupCommands, resolveHiddenProbeCwd } from './hidden-probe-cwd'
import { quotePosixShell } from '../../shared/shell-argument-quoting'
import { resolveWslInteropSpawnCwd } from '../wsl-interop-spawn-directory'
@@ -54,7 +51,7 @@ function buildWslCodexCommand(
return null
}
const setupCommands = [
...getHiddenRateLimitWslCwdSetupCommands(),
...getHiddenProbeWslCwdSetupCommands(),
`export CODEX_HOME=${quotePosixShell(wslInfo.linuxPath)}`
].join(' && ')
const execSuffix = `${args.map(quotePosixShell).join(' ')} <&3 >&4 3<&- 4>&-`
@@ -99,7 +96,7 @@ async function fetchViaRpc(options?: CodexRateLimitFetchOptions): Promise<Provid
program: wslCodex ? wslCodex.command : codexCommand,
args: wslCodex ? wslCodex.args : codexArgs,
// wsl.exe holds cwd busy for ~60s; keep probes outside profile folders (#29845).
cwd: wslCodex ? resolveWslInteropSpawnCwd() : resolveHiddenRateLimitPtyCwd(),
cwd: wslCodex ? resolveWslInteropSpawnCwd() : resolveHiddenProbeCwd(),
env: withCliRuntimeOnPath(codexCommand, {
...(wslCodex ? processEnvWithoutCodexHome() : process.env),
// WSL probes keep the guest's own proxy env; a Windows proxy address may not resolve there.
@@ -4,8 +4,9 @@ import { join } from 'node:path'
import { isRootLikePath } from '../providers/pty-path-safety'
const HIDDEN_RATE_LIMIT_PTY_CWD_DIR = 'rate-limit-pty-cwd'
const WSL_RATE_LIMIT_PTY_CWD_DIR = 'orca-rate-limit-pty-cwd'
// Folder names predate the generic probe naming; keep them so existing dirs are reused.
const HIDDEN_PROBE_CWD_DIR = 'rate-limit-pty-cwd'
const WSL_HIDDEN_PROBE_CWD_DIR = 'orca-rate-limit-pty-cwd'
// Never let hidden Claude discovery start at a filesystem root; bound cwd to userData or tmpdir (runaway-cpu-hidden-usage-pty-design.md).
function resolveUserDataRoot(userDataPath?: string | null): string {
@@ -16,19 +17,19 @@ function resolveUserDataRoot(userDataPath?: string | null): string {
return join(tmpdir(), 'orca-rate-limit-pty')
}
export function resolveHiddenRateLimitPtyCwd(options?: { userDataPath?: string | null }): string {
const cwd = join(resolveUserDataRoot(options?.userDataPath), HIDDEN_RATE_LIMIT_PTY_CWD_DIR)
export function resolveHiddenProbeCwd(options?: { userDataPath?: string | null }): string {
const cwd = join(resolveUserDataRoot(options?.userDataPath), HIDDEN_PROBE_CWD_DIR)
mkdirSync(cwd, { recursive: true })
const realCwd = realpathSync(cwd)
if (isRootLikePath(realCwd) || !statSync(realCwd).isDirectory()) {
throw new Error(`Hidden rate-limit PTY cwd is not a safe directory: ${realCwd}`)
throw new Error(`Hidden probe cwd is not a safe directory: ${realCwd}`)
}
return realCwd
}
export function getHiddenRateLimitWslCwdSetupCommands(): string[] {
export function getHiddenProbeWslCwdSetupCommands(): string[] {
return [
`orca_rate_limit_cwd="\${TMPDIR:-/tmp}/${WSL_RATE_LIMIT_PTY_CWD_DIR}"`,
`orca_rate_limit_cwd="\${TMPDIR:-/tmp}/${WSL_HIDDEN_PROBE_CWD_DIR}"`,
'mkdir -p "$orca_rate_limit_cwd"',
'cd "$orca_rate_limit_cwd"'
]
@@ -44,14 +44,15 @@ function session(
describe('dropStrandedSleepRecord', () => {
it("drops a workspace sleep's record once no tab holds its pane", () => {
expect(
dropStrandedSleepRecord(session([]), slept.paneKey)?.sleepingAgentSessionsByPaneKey
dropStrandedSleepRecord(session([]), 'wt-1', slept.paneKey)?.sleepingAgentSessionsByPaneKey
).toEqual({})
})
// A window must not discard a record main still resumes when its tab opens.
it('keeps a record whose tab is still listed, and window-owned records', () => {
expect(dropStrandedSleepRecord(session(['tab-1']), slept.paneKey)).toBeNull()
it("keeps a listed tab's record, window-owned records, and another worktree's record", () => {
expect(dropStrandedSleepRecord(session(['tab-1']), 'wt-1', slept.paneKey)).toBeNull()
const quit = { ...slept, origin: 'quit' as const }
expect(dropStrandedSleepRecord(session([], quit), quit.paneKey)).toBeNull()
expect(dropStrandedSleepRecord(session([], quit), 'wt-1', quit.paneKey)).toBeNull()
expect(dropStrandedSleepRecord(session([]), 'wt-2', slept.paneKey)).toBeNull()
})
})
@@ -163,6 +163,7 @@ export function clearClosedWorktreeResumeRecords(
/** The session without a workspace-sleep record whose tab it no longer lists, or null when the record is still placed. */
export function dropStrandedSleepRecord(
session: WorkspaceSessionState,
worktreeId: string,
paneKey: string
): WorkspaceSessionState | null {
const record = session.sleepingAgentSessionsByPaneKey?.[paneKey]
@@ -170,7 +171,7 @@ export function dropStrandedSleepRecord(
const tabListed = Object.values(session.tabsByWorktree).some((tabs) =>
tabs.some((tab) => tab.id === tabId)
)
if (!record || !isWorkspaceSleepRecord(record) || tabListed) {
if (!record || record.worktreeId !== worktreeId || !isWorkspaceSleepRecord(record) || tabListed) {
return null
}
const { [paneKey]: _dropped, ...rest } = session.sleepingAgentSessionsByPaneKey ?? {}
@@ -270,7 +270,7 @@ export abstract class OrcaRuntimeWithListManagedWorktrees extends OrcaRuntimeWit
async retireStrandedSleepRecord(worktreeId: string, paneKey: string): Promise<void> {
await this.commitWorkspaceSessionEdit(
this.getWorkspaceSessionHostIdForWorktree(worktreeId),
(session) => dropStrandedSleepRecord(session, paneKey)
(session) => dropStrandedSleepRecord(session, worktreeId, paneKey)
)
}
@@ -0,0 +1,16 @@
import { createHash } from 'node:crypto'
const revisions = new Map<(version: string) => string, string>()
/** The bundle hash alone misses launcher edits; a new guest dir forces one reinstall of launch.sh. */
export function withWslGuestLauncherRevision(
bundleVersion: string,
buildLaunchScript: (version: string) => string
): string {
let revision = revisions.get(buildLaunchScript)
if (!revision) {
revision = createHash('sha256').update(buildLaunchScript('0')).digest('hex').slice(0, 8)
revisions.set(buildLaunchScript, revision)
}
return `${bundleVersion}.${revision}`
}
+1 -3
View File
@@ -14,8 +14,7 @@ import {
import {
buildPosixHookPayloadCapture,
buildPosixHookSpoolLines,
buildWindowsHookEnvironmentGuardLines,
buildWindowsHookStdinDrainEpilogue
buildWindowsHookEnvironmentGuardLines
} from '../agent-hooks/hook-stdin-contract'
import { buildPosixAgentHookPostCommand } from '../agent-hooks/hook-post-command'
import {
@@ -49,7 +48,6 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string {
...buildWindowsHookEnvironmentGuardLines(),
buildWindowsAgentHookCurlPostCommand('zcode'),
'exit /b 0',
...buildWindowsHookStdinDrainEpilogue(),
''
].join('\r\n')
}
@@ -31,7 +31,7 @@ import { hasCtrlEnterCsiUAuthorityForPane } from './terminal-ctrl-enter'
import { titleHasExplicitAgentIdentity } from './title-agent-identity'
import { isTuiAgent } from '../../../../shared/tui-agent-config'
import { resolveTerminalInputHostPlatform } from './terminal-input-host-platform'
import { isLocalWindowsConptyPaneForCtrlArrow } from './terminal-ctrl-arrow-conpty'
import { isTerminalPaneLocalWindowsConpty } from './terminal-pane-local-windows-conpty'
import { keyboardEventBelongsToScope } from './terminal-keyboard-scope'
import {
isEditableTarget,
@@ -135,7 +135,7 @@ export function createTerminalKeyboardRuntime(options: RuntimeOptions) {
if (!pane) {
return false
}
return isLocalWindowsConptyPaneForCtrlArrow({
return isTerminalPaneLocalWindowsConpty({
isWindows,
userAgent: navigator.userAgent,
state: useAppStore.getState(),
@@ -1,16 +1,14 @@
import { describe, expect, it } from 'vitest'
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants'
import {
isLocalWindowsConptyPaneForCtrlArrow,
type TerminalCtrlArrowConptyState
} from './terminal-ctrl-arrow-conpty'
isTerminalPaneLocalWindowsConpty,
type TerminalPaneConptyState
} from './terminal-pane-local-windows-conpty'
import type { PtyTransport } from './pty-transport-types'
const WINDOWS_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
function state(
overrides: Partial<TerminalCtrlArrowConptyState> = {}
): TerminalCtrlArrowConptyState {
function state(overrides: Partial<TerminalPaneConptyState> = {}): TerminalPaneConptyState {
return {
repos: [],
worktreesByRepo: {},
@@ -43,10 +41,10 @@ function remoteRuntimeTransport(): Pick<
}
}
describe('isLocalWindowsConptyPaneForCtrlArrow', () => {
describe('isTerminalPaneLocalWindowsConpty', () => {
it('uses live local session metadata for synthetic floating terminals with no repo record', () => {
expect(
isLocalWindowsConptyPaneForCtrlArrow({
isTerminalPaneLocalWindowsConpty({
isWindows: true,
userAgent: WINDOWS_UA,
state: state({
@@ -67,7 +65,7 @@ describe('isLocalWindowsConptyPaneForCtrlArrow', () => {
it('keeps an already-launched local PowerShell pane local after store ownership changes', () => {
const worktreeId = 'repo-1::C:\\repo'
expect(
isLocalWindowsConptyPaneForCtrlArrow({
isTerminalPaneLocalWindowsConpty({
isWindows: true,
userAgent: WINDOWS_UA,
state: state({
@@ -88,7 +86,7 @@ describe('isLocalWindowsConptyPaneForCtrlArrow', () => {
it('does not treat a live remote-runtime terminal as local ConPTY', () => {
expect(
isLocalWindowsConptyPaneForCtrlArrow({
isTerminalPaneLocalWindowsConpty({
isWindows: true,
userAgent: WINDOWS_UA,
state: state(),
@@ -104,7 +102,7 @@ describe('isLocalWindowsConptyPaneForCtrlArrow', () => {
it('stays conservative for unresolved repo-backed panes before a live session exists', () => {
expect(
isLocalWindowsConptyPaneForCtrlArrow({
isTerminalPaneLocalWindowsConpty({
isWindows: true,
userAgent: WINDOWS_UA,
state: state(),
@@ -15,28 +15,28 @@ type TerminalTabShellState = {
>
}
export type TerminalCtrlArrowConptyState = Parameters<typeof getConnectionIdFromState>[0] &
export type TerminalPaneConptyState = Parameters<typeof getConnectionIdFromState>[0] &
WorktreeRuntimeOwnerState &
TerminalTabShellState
type TerminalCtrlArrowConptyTransport = Pick<
type TerminalPaneConptyTransport = Pick<
PtyTransport,
'getPtyId' | 'getConnectionId' | 'getLocalSessionMetadata'
>
type TerminalCtrlArrowConptyArgs = {
type TerminalPaneConptyArgs = {
isWindows: boolean
userAgent: string
state: TerminalCtrlArrowConptyState
state: TerminalPaneConptyState
worktreeId: string
tabId: string
paneId: number
paneCwd: PaneCwdMap
fallbackCwd: string
transport: TerminalCtrlArrowConptyTransport | null
transport: TerminalPaneConptyTransport | null
}
export function isLocalWindowsConptyPaneForCtrlArrow({
export function isTerminalPaneLocalWindowsConpty({
isWindows,
userAgent,
state,
@@ -46,7 +46,7 @@ export function isLocalWindowsConptyPaneForCtrlArrow({
paneCwd,
fallbackCwd,
transport
}: TerminalCtrlArrowConptyArgs): boolean {
}: TerminalPaneConptyArgs): boolean {
if (!isWindows) {
return false
}