mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(windows): mark PowerShell startup command boundaries with OSC 133 C/D (#18182)
A startup command embedded in the PowerShell -EncodedCommand payload runs before the first prompt, and the prompt wrapper suppresses 133;D until it has printed once. An agent launched this way exited with no command-finished boundary, so its pane was never retired. Emit 133;C before the embedded startup command and prime the prompt latch so the first prompt reports 133;D with the exit code. Stdin-typed startup commands keep readline's own marks. The encoded-command budget grows by the mark's size so real commands keep the prior limit. Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
This commit is contained in:
@@ -93,6 +93,11 @@ export function getPowerShellOsc133Bootstrap(): string {
|
||||
return POWERSHELL_OSC133_BOOTSTRAP_PAYLOAD
|
||||
}
|
||||
|
||||
// Startup commands run before the first prompt, which suppresses 133;D; mark them like typed commands.
|
||||
export function getPowerShellOsc133StartupCommandMark(): string {
|
||||
return 'if (Test-Path variable:global:__OrcaOsc133State) { [Console]::Write("$([char]27)]133;C$([char]7)"); $Global:__OrcaOsc133State.HasSeenPrompt = $true }'
|
||||
}
|
||||
|
||||
export function isPowerShellExecutableName(shellName: string): boolean {
|
||||
const normalized = shellName.toLowerCase()
|
||||
return (
|
||||
|
||||
@@ -9,6 +9,7 @@ import { resolveWindowsShellLaunchArgs } from './windows-shell-args'
|
||||
import { getShellReadyWrapperRoot } from './local-pty-shell-ready-wrapper-root'
|
||||
|
||||
const CODEX_LAUNCH_PREFLIGHT = 'C:\\Program Files\\Orca\\orca.exe'
|
||||
const POWERSHELL_STARTUP_COMMAND_C_MARK = '[Console]::Write("$([char]27)]133;C$([char]7)")'
|
||||
const CMD_CODEX_LAUNCH_PREFLIGHT =
|
||||
'if defined ORCA_CODEX_LAUNCH_PREFLIGHT call %ORCA_CODEX_LAUNCH_PREFLIGHT_CMD_QUOTE%%ORCA_CODEX_LAUNCH_PREFLIGHT%%ORCA_CODEX_LAUNCH_PREFLIGHT_CMD_QUOTE% agent hooks prepare-codex > nul 2>&1'
|
||||
|
||||
@@ -200,6 +201,10 @@ describe('resolveWindowsShellLaunchArgs', () => {
|
||||
expect(command).toContain('function Global:prompt')
|
||||
expect(command).toContain(expectedPowerShellRestoreCwdCommand("'C:\\Users\\alice'"))
|
||||
expect(command.trimEnd().endsWith("& 'codex' '--no-alt-screen'")).toBe(true)
|
||||
// Without the mark, the first prompt after the agent exits withholds 133;D (#18180).
|
||||
expect(command).toContain(
|
||||
`${POWERSHELL_STARTUP_COMMAND_C_MARK}; $Global:__OrcaOsc133State.HasSeenPrompt = $true }\n& 'codex' '--no-alt-screen'`
|
||||
)
|
||||
})
|
||||
|
||||
it('preserves complex PowerShell startup command text through EncodedCommand', () => {
|
||||
@@ -230,9 +235,10 @@ describe('resolveWindowsShellLaunchArgs', () => {
|
||||
|
||||
expect(result.startupCommandDeliveredInShellArgs).toBeUndefined()
|
||||
expect(result.shellArgs).toEqual(['-NoLogo', '-NoExit', '-EncodedCommand', expect.any(String)])
|
||||
expect(decodePowerShellCommand(result)).toContain(
|
||||
expectedPowerShellRestoreCwdCommand("'C:\\Users\\alice'")
|
||||
)
|
||||
const command = decodePowerShellCommand(result)
|
||||
expect(command).toContain(expectedPowerShellRestoreCwdCommand("'C:\\Users\\alice'"))
|
||||
// Stdin-typed commands get readline's 133;C; an early latch would emit a false 133;D.
|
||||
expect(command).not.toContain(POWERSHELL_STARTUP_COMMAND_C_MARK)
|
||||
})
|
||||
|
||||
it('handles pwsh.exe (PowerShell Core) the same as Windows PowerShell', () => {
|
||||
|
||||
@@ -10,14 +10,16 @@ import { getBashWrapperLaunchArgs } from './local-pty-shell-ready'
|
||||
import { ensureShellReadyWrappersAt } from './local-pty-shell-ready-wrapper-generation'
|
||||
import {
|
||||
encodePowerShellCommand,
|
||||
getPowerShellOsc133Bootstrap
|
||||
getPowerShellOsc133Bootstrap,
|
||||
getPowerShellOsc133StartupCommandMark
|
||||
} from '../powershell-osc133-bootstrap'
|
||||
import { quoteStartupArg } from '../../shared/tui-agent-startup-shell'
|
||||
|
||||
/** cmd.exe's own documented ceiling; callers that go through sshd budget below it. */
|
||||
export const CMD_EXE_COMMAND_LINE_MAX_CHARS = 8191
|
||||
const STARTUP_COMMAND_TEXT_MAX_CHARS = 6000
|
||||
const POWERSHELL_ENCODED_COMMAND_ARG_MAX_CHARS = 28_000
|
||||
// Includes ~400 encoded chars for the startup command mark so real commands keep the prior budget.
|
||||
const POWERSHELL_ENCODED_COMMAND_ARG_MAX_CHARS = 28_400
|
||||
const CMD_UTF8_SETUP_COMMAND = 'chcp 65001 > nul'
|
||||
export const ORCA_CODEX_LAUNCH_PREFLIGHT_CMD_QUOTE_ENV = 'ORCA_CODEX_LAUNCH_PREFLIGHT_CMD_QUOTE'
|
||||
const CMD_CODEX_LAUNCH_PREFLIGHT = `if defined ORCA_CODEX_LAUNCH_PREFLIGHT call %${ORCA_CODEX_LAUNCH_PREFLIGHT_CMD_QUOTE_ENV}%%ORCA_CODEX_LAUNCH_PREFLIGHT%%${ORCA_CODEX_LAUNCH_PREFLIGHT_CMD_QUOTE_ENV}% agent hooks prepare-codex > nul 2>&1`
|
||||
@@ -88,7 +90,7 @@ function getPowerShellEncodedCommand(
|
||||
return { encodedCommand: encodePowerShellCommand(bootstrap) }
|
||||
}
|
||||
|
||||
const command = `${bootstrap}\n${startupCommand}`
|
||||
const command = `${bootstrap}\n${getPowerShellOsc133StartupCommandMark()}\n${startupCommand}`
|
||||
const encodedCommand = encodePowerShellCommand(command)
|
||||
// UTF-16/base64 expands payloads; leave margin under CreateProcess 32,767-character limit.
|
||||
if (encodedCommand.length > POWERSHELL_ENCODED_COMMAND_ARG_MAX_CHARS) {
|
||||
|
||||
@@ -52,7 +52,7 @@ export function observeTerminalCommandPaint(
|
||||
record.commandPaint.observe(data, normalizedText)
|
||||
}
|
||||
|
||||
/** Without markers (older fish, relay zsh, unwrapped shells, Windows command args), any output is the only paint evidence. */
|
||||
/** Without markers (older fish, relay zsh, unwrapped shells, cmd.exe /K startup args), any output is the only paint evidence. */
|
||||
export function hasTerminalCommandPainted(record: TerminalCommandPaintRecord): boolean {
|
||||
return record.commandPaint?.hasPainted() ?? true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user