fix(omp): guard fresh launches with execution-host settings

This commit is contained in:
Neil
2026-09-19 01:37:23 -07:00
parent 2f07add793
commit fd2481a32e
17 changed files with 426 additions and 44 deletions
+51
View File
@@ -0,0 +1,51 @@
# Fresh OMP launches
Orca's new-session and draft launch plans apply a one-time `--config` overlay
containing `autoResume: false`. OMP's configured session directory, settings,
authentication and extensions remain in their usual locations. Saved launch
configuration omits the overlay so explicit resume keeps its normal semantics.
Custom commands with session selectors, unknown flags, positional arguments or
shell compounds are left unchanged.
The execution host creates the overlay. Local and WSL terminals use Orca userData
(with WSLENV path translation); SSH relays use their own managed directory. Config
creation is independent of status-hook preferences and does not require plugin
source installation. An unavailable file produces a terminal diagnostic and skips
OMP. Filesystem failures do not prevent unrelated agents or bare shells starting.
The guard invokes OMP in the current shell, preserving functions, aliases and the
managed status wrapper. A nonzero agent exit never triggers a second launch.
POSIX commands also support fish; environment presence is checked before expansion
so an old host under `set -u` reports the same missing-settings diagnostic.
## Mixed versions
The existing command and environment transport carries the launch unchanged; no
new RPC or stream opcode is introduced. A new host exports the path before shell
startup, including bare shells that receive an OMP command later. An old client
continues its existing launch behavior on a new host. A new fresh-launch command
on an old host without the managed environment fails visibly and requests a host
update and terminal restart. It must not silently fall back to OMP auto-resume.
## Verification
`src/shared/omp-fresh-launch-shell.test.ts` runs actual available bash, zsh and fish
shells, checking exact argv, a single invocation, nonzero exit, deleted settings
and an absent environment variable. `src/relay/omp-fresh-launch-environment.test.ts`
checks the guarded command through relay environment assembly and the actual OMP
shell wrapper, retaining both extension and config arguments and prefill.
Local host assembly tests recognize guarded POSIX, cmd and PowerShell commands.
Run the actual OMP storage smoke against a read-only OMP checkout:
```sh
ORCA_BACKGROUND_LAUNCH=1 bun tests/tools/omp-fresh-session-runtime-smoke.mjs /path/to/oh-my-pi
```
For Windows, bundle `tests/tools/omp-fresh-launch-windows-smoke.ts` with
`bun build --target=node --outfile=/tmp/omp-fresh-windows-smoke.mjs`, transfer the
bundle to the host and run it using Node with `ORCA_BACKGROUND_LAUNCH=1`.
The smoke uses temporary files and process-local environment only. Both cmd and
PowerShell must pass existing/missing/unset/directory settings cases, preserving
exit 17 for the single successful launch and returning exit 1 without launching
when settings are unavailable. This passed on Windows host `awin` on 2026-09-14.
+1
View File
@@ -129,6 +129,7 @@ export const agentHookServerModuleMock = () => ({
export const piTitlebarExtensionModuleMock = () => ({
piTitlebarExtensionService: {
buildPtyEnv: piBuildPtyEnvMock,
buildFreshOmpEnv: () => ({ ORCA_OMP_FRESH_CONFIG: '/tmp/orca-fresh-session.yml' }),
clearPty: piClearPtyMock
}
})
@@ -1,3 +1,4 @@
import { withFreshOmpLaunch } from '../../shared/omp-fresh-launch'
import { describe, expect, it, vi } from 'vitest'
import {
readFileSyncMock,
@@ -293,14 +294,19 @@ describe('registerPtyHandlers', () => {
})
expect(env.ORCA_PI_SOURCE_AGENT_DIR).toBe('/tmp/default-pi-agent')
})
it('threads command: "omp" through to piBuildPtyEnv and emits OMP status metadata', async () => {
it.each([
'omp',
withFreshOmpLaunch('omp', 'posix'),
withFreshOmpLaunch('omp', 'powershell'),
withFreshOmpLaunch('omp', 'cmd')
])('threads OMP command %s through to the host integration', async (command) => {
// Why: OMP launches emit ORCA_OMP_* shadow vars, not Pi-named ones; only PI_CODING_AGENT_DIR stays (OMP's own binary reads it).
const env = await spawnAndGetEnv(
undefined,
{ PI_CODING_AGENT_DIR: '/tmp/user-omp-agent' },
undefined,
undefined,
'omp'
command
)
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(
expect.any(String),
@@ -1,3 +1,4 @@
import { withFreshOmpLaunch } from '../../shared/omp-fresh-launch'
import { describe, expect, it, vi } from 'vitest'
import { spawnMock } from './pty-ipc-mock-registry'
import { BUNDLED_CLI_PATH, TEST_CODEX_HOME, makeDisposable } from './pty-ipc-test-constants'
@@ -58,6 +59,22 @@ describe('registerPtyHandlers', () => {
const { handlers, mainWindow, spawnAndGetEnv, withBundledCli } = setupPtyIpcSuite()
describe('spawn environment', () => {
it('prepares fresh OMP settings even when status hooks are disabled', () => {
const env = buildPtyHostEnv(
'fresh-without-hooks',
{ ORCA_OMP_FRESH_CONFIG: '/other-host/stale.yml' },
{
isPackaged: true,
userDataPath: '/tmp/orca-user-data',
selectedCodexHomePath: null,
agentStatusHooksEnabled: false,
launchCommand: withFreshOmpLaunch('omp', 'posix')
}
)
expect(env.ORCA_OMP_FRESH_CONFIG).toBe('/tmp/orca-fresh-session.yml')
expect(env.ORCA_OMP_STATUS_EXTENSION).toBeUndefined()
})
it('routes headless browser launches through the owning Orca workspace', () => {
const inheritedBrowser = process.env.BROWSER
delete process.env.BROWSER
+3
View File
@@ -194,6 +194,9 @@ export function buildPtyHostEnv(
overlay: 'ORCA_OMP_CODING_AGENT_DIR',
source: 'ORCA_OMP_SOURCE_AGENT_DIR'
})
if (shouldPrepareOmpShadow) {
Object.assign(baseEnv, piTitlebarExtensionService.buildFreshOmpEnv())
}
delete baseEnv.ORCA_OMP_STATUS_EXTENSION
delete baseEnv.ORCA_PRIME_AGENT_SOURCE_AGENT_DIR
delete baseEnv.ORCA_PRIME_AGENT_STATUS_EXTENSION
+11 -19
View File
@@ -1,8 +1,4 @@
import {
OMP_FRESH_CONFIG_FILENAME,
OMP_FRESH_CONFIG_SOURCE,
ORCA_OMP_FRESH_CONFIG_ENV
} from '../../shared/omp-fresh-launch'
import { materializeOmpFreshConfig } from '../../shared/omp-fresh-config'
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { homedir } from 'node:os'
import { join } from 'node:path'
@@ -179,26 +175,22 @@ export class PiTitlebarExtensionService {
}
}
buildFreshOmpEnv(): Record<string, string> {
return {
ORCA_OMP_FRESH_CONFIG: materializeOmpFreshConfig(
join(getAppEnvironment().getPath('userData'), OMP_MANAGED_STATUS_EXTENSION_DIR)
)
}
}
buildPtyEnv(
ptyId: string,
existingAgentDir: string | undefined,
kind: PiAgentKind,
options?: { materializeDefaultHome?: boolean; configDirName?: string }
): Record<string, string> {
// The caller resolves the effective launch environment. Reading the
// daemon's ambient PI_CONFIG_DIR here can select the host profile for a
// guest/WSL launch whose environment has not been hydrated yet.
const freshConfigEnv: Record<string, string> = {}
if (kind === 'omp') {
const configDir = join(
getAppEnvironment().getPath('userData'),
OMP_MANAGED_STATUS_EXTENSION_DIR
)
mkdirSync(configDir, { recursive: true })
const configPath = join(configDir, OMP_FRESH_CONFIG_FILENAME)
writeFileSync(configPath, OMP_FRESH_CONFIG_SOURCE)
freshConfigEnv[ORCA_OMP_FRESH_CONFIG_ENV] = configPath
}
const freshConfigEnv = kind === 'omp' ? this.buildFreshOmpEnv() : {}
// The caller resolves the effective launch environment before this point.
const sourceAgentDir =
existingAgentDir || getDefaultPiAgentDir(kind, options?.configDirName)
if (kind !== 'prime-agent') {
@@ -0,0 +1,107 @@
import { runProcess } from '../shared/child-process/run-process'
import { getPosixOmpShellWrapper } from '../main/pty/omp-shell-wrapper'
import { mkdtempSync, readFileSync, rmSync, existsSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import type * as NodeOs from 'node:os'
import { delimiter, join } from 'node:path'
import { afterEach, expect, it, vi } from 'vitest'
import { RelayDispatcher } from './dispatcher'
import { PtyHandler } from './pty-handler'
import { RelayAgentHookRuntime } from './relay-agent-hook-runtime'
import { PluginOverlayManager } from './plugin-overlay'
import { withFreshOmpLaunch } from '../shared/omp-fresh-launch'
const state = vi.hoisted(() => ({ home: '' }))
vi.mock('node:os', async (original) => ({
...(await original<typeof NodeOs>()),
homedir: () => state.home
}))
afterEach(() => {
vi.restoreAllMocks()
if (state.home) {
rmSync(state.home, { recursive: true, force: true })
}
})
it('prepares the execution host OMP config and status extension for a guarded launch', async () => {
state.home = mkdtempSync(join(tmpdir(), 'orca-relay-fresh-'))
const dispatcher = new RelayDispatcher(() => {})
const handler = new PtyHandler(dispatcher)
const augment = vi.spyOn(handler, 'addEnvAugmenter')
const source = vi.spyOn(PluginOverlayManager.prototype, 'hasPiSource').mockReturnValue(true)
const original = PluginOverlayManager.prototype.materializePi
vi.spyOn(PluginOverlayManager.prototype, 'materializePi').mockImplementation(function (
this: PluginOverlayManager,
...args
) {
this.setSources({ ompExtensionSource: '// Orca managed test status extension' })
return original.apply(this, args)
})
const runtime = new RelayAgentHookRuntime(
dispatcher,
handler,
join(state.home, 'relay.sock'),
join(state.home, 'hooks')
)
await runtime.start()
try {
const environment = augment.mock.calls[1][0]({
id: 'fresh-pane',
shell: '/bin/bash',
env: { HOME: state.home },
command: withFreshOmpLaunch('omp', 'posix')
})
expect(readFileSync(environment.ORCA_OMP_FRESH_CONFIG, 'utf8')).toBe('autoResume: false\n')
expect(existsSync(environment.ORCA_OMP_STATUS_EXTENSION)).toBe(true)
expect(environment.ORCA_OMP_SOURCE_AGENT_DIR).toBe(join(state.home, '.omp', 'agent'))
expect(environment.PI_CODING_AGENT_DIR).toBeUndefined()
expect(environment.ORCA_PI_SOURCE_AGENT_DIR).toBeUndefined()
if (process.platform !== 'win32' && existsSync('/bin/bash')) {
writeFileSync(
join(state.home, 'omp'),
'#!/bin/sh\nprintf "%s\\n" "$@" "$ORCA_OMP_PREFILL"\nexit 17\n',
{ mode: 0o755 }
)
const result = await runProcess({
program: '/bin/bash',
args: [
'--noprofile',
'--norc',
'-c',
`${getPosixOmpShellWrapper()}\n${withFreshOmpLaunch('omp', 'posix')}`
],
cwd: state.home,
env: {
...process.env,
...environment,
PATH: state.home + delimiter + process.env.PATH,
ORCA_OMP_PREFILL: 'remote draft'
}
})
expect(result.code).toBe(17)
expect(result.stdout.trim().split('\n')).toEqual([
'--extension',
environment.ORCA_OMP_STATUS_EXTENSION,
'--config',
environment.ORCA_OMP_FRESH_CONFIG,
'remote draft'
])
}
source.mockReturnValue(false)
expect(
augment.mock.calls[1][0]({ id: 'other', shell: '/bin/bash', env: {}, command: 'codex' })
).toEqual({})
} finally {
runtime.stop()
dispatcher.dispose()
}
})
it('fails the config guard safely when the relay-owned directory cannot be created', () => {
state.home = mkdtempSync(join(tmpdir(), 'orca-relay-fresh-fail-'))
const blocked = join(state.home, 'file')
writeFileSync(blocked, '')
const manager = new PluginOverlayManager({ homeDir: blocked })
expect(manager.materializeOmpFreshConfig()).toBe('')
})
+7
View File
@@ -1,3 +1,4 @@
import { materializeOmpFreshConfig } from '../shared/omp-fresh-config'
// Why: relay-side equivalent of Orca's local agent integration installers.
// OpenCode still needs a config overlay, while Pi/OMP now get Orca-managed
// extension files installed into the remote agent homes. Host paths from the
@@ -275,6 +276,12 @@ export class PluginOverlayManager {
}
}
materializeOmpFreshConfig(): string {
return materializeOmpFreshConfig(
join(this.homeDir, RELAY_HOOKS_DIR, OMP_MANAGED_STATUS_EXTENSION_DIR)
)
}
/** Install the Pi/OMP status extension into the remote real agent dir.
* `kind` selects which Pi-compatible agent's default dir to use when
* `existingAgentDir` is not supplied.
+6 -3
View File
@@ -94,9 +94,6 @@ export class RelayAgentHookRuntime {
}
}
}
if (!this.pluginOverlay.hasPiSource()) {
return env
}
const launchCommandHint = resolveSetupAgentSequenceLaunchCommand(context.env, context.command)
const explicitKind = isPiCompatibleAgentType(context.launchAgent)
? context.launchAgent
@@ -106,6 +103,12 @@ export class RelayAgentHookRuntime {
const kind = explicitKind ?? 'pi'
const hasLaunchCommand =
typeof launchCommandHint === 'string' && launchCommandHint.trim().length > 0
if (kind === 'omp' || !hasLaunchCommand) {
env.ORCA_OMP_FRESH_CONFIG = this.pluginOverlay.materializeOmpFreshConfig()
}
if (!this.pluginOverlay.hasPiSource()) {
return env
}
if (kind === 'pi') {
const sourceDir = resolvePiSourceAgentDir(context.env, context.shell, 'pi')
const result = this.pluginOverlay.materializePi(overlayId, sourceDir, 'pi', {
+18
View File
@@ -0,0 +1,18 @@
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { OMP_FRESH_CONFIG_FILENAME, OMP_FRESH_CONFIG_SOURCE } from './omp-fresh-launch'
/** An empty path fails the launch guard without preventing unrelated terminal launches. */
export function materializeOmpFreshConfig(directory: string): string {
try {
const configPath = join(directory, OMP_FRESH_CONFIG_FILENAME)
if (existsSync(configPath) && readFileSync(configPath, 'utf8') === OMP_FRESH_CONFIG_SOURCE) {
return configPath
}
mkdirSync(directory, { recursive: true })
writeFileSync(configPath, OMP_FRESH_CONFIG_SOURCE)
return configPath
} catch {
return ''
}
}
+74
View File
@@ -0,0 +1,74 @@
import { existsSync } from 'node:fs'
import { delimiter } from 'node:path'
import { mkdtemp, writeFile, readFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, expect, it } from 'vitest'
import { runProcess } from './child-process/run-process'
import { buildAgentStartupPlan } from './tui-agent-startup'
const roots: string[] = []
afterEach(async () => {
for (const root of roots.splice(0)) {
await rm(root, { recursive: true, force: true })
}
})
const shells = ['bash', 'zsh', 'fish'].filter(
(shell) =>
process.platform !== 'win32' &&
(process.env.PATH ?? '')
.split(delimiter)
.some((directory) => existsSync(join(directory, shell)))
)
it.each(shells)(
'preserves same-shell function argv, one launch and exit status in %s',
async (shell) => {
const root = await mkdtemp(join(tmpdir(), 'orca-fresh-shell-'))
roots.push(root)
const config = join(root, 'fresh settings.yml')
const capture = join(root, 'argv')
await writeFile(config, 'autoResume: false\n')
const plan = buildAgentStartupPlan({
agent: 'omp',
prompt: 'task with spaces',
cmdOverrides: {},
platform: 'linux',
isRemote: true
})
expect(plan).not.toBeNull()
const define =
shell === 'fish'
? 'function omp; printf "%s\\n" $argv >> "$CAPTURE"; return 17; end; '
: 'omp() { printf "%s\\n" "$@" >> "$CAPTURE"; return 17; }; '
const result = await runProcess({
program: shell,
args: ['-c', define + plan!.launchCommand],
cwd: root,
env: { ...process.env, ORCA_OMP_FRESH_CONFIG: config, CAPTURE: capture }
})
expect(result.code).toBe(17)
expect(await readFile(capture, 'utf8')).toBe(`--config\n${config}\ntask with spaces\n`)
await rm(capture)
await rm(config)
const missing = await runProcess({
program: shell,
args: ['-c', define + plan!.launchCommand],
cwd: root,
env: { ...process.env, ORCA_OMP_FRESH_CONFIG: config, CAPTURE: capture }
})
expect(missing.code).not.toBe(0)
expect(missing.stderr).toContain('fresh OMP settings are unavailable')
await expect(readFile(capture)).rejects.toMatchObject({ code: 'ENOENT' })
const unsetEnv: NodeJS.ProcessEnv = { ...process.env, CAPTURE: capture }
delete unsetEnv.ORCA_OMP_FRESH_CONFIG
const unset = await runProcess({
program: shell,
args: ['-c', (shell === 'fish' ? '' : 'set -u; ') + define + plan!.launchCommand],
cwd: root,
env: unsetEnv
})
expect(unset.code).not.toBe(0)
expect(unset.stderr).toContain('fresh OMP settings are unavailable')
await expect(readFile(capture)).rejects.toMatchObject({ code: 'ENOENT' })
}
)
+6 -6
View File
@@ -6,7 +6,7 @@ describe('OMP fresh launch intent', () => {
it.each(['omp', 'omp launch', 'omp --model provider/model', 'omp --config user.yml'])(
'%s adds the final overlay',
(command) => {
expect(withFreshOmpLaunch(command, 'posix')).toBe(
expect(withFreshOmpLaunch(command, 'posix')).toContain(
`${command} --config "$ORCA_OMP_FRESH_CONFIG"`
)
}
@@ -29,13 +29,13 @@ describe('OMP fresh launch intent', () => {
'echo omp',
'omp && echo hi'
])('preserves %s', (command) => {
expect(withFreshOmpLaunch(command, 'posix')).toBe(command)
expect(withFreshOmpLaunch(command, 'posix')).toContain(command)
})
it('quotes the host config path for each Windows shell', () => {
expect(withFreshOmpLaunch('omp', 'powershell')).toBe(
expect(withFreshOmpLaunch('omp', 'powershell')).toContain(
'omp --config "$env:ORCA_OMP_FRESH_CONFIG"'
)
expect(withFreshOmpLaunch('omp', 'cmd')).toBe('omp --config "%ORCA_OMP_FRESH_CONFIG%"')
expect(withFreshOmpLaunch('omp', 'cmd')).toContain('omp --config "%ORCA_OMP_FRESH_CONFIG%"')
})
it('keeps fresh intent out of saved resume command and environment', () => {
const plan = buildAgentStartupPlan({
@@ -48,7 +48,7 @@ describe('OMP fresh launch intent', () => {
expect(JSON.stringify(plan?.launchConfig)).not.toContain('ORCA_OMP_FRESH_CONFIG')
expect(plan?.env).toBeUndefined()
})
it('does not require a new environment field from an older SSH relay', () => {
it('requires host-owned configuration for fresh SSH launches', () => {
const plan = buildAgentStartupPlan({
agent: 'omp',
prompt: 'new task',
@@ -56,6 +56,6 @@ describe('OMP fresh launch intent', () => {
platform: 'linux',
isRemote: true
})
expect(plan?.launchCommand).not.toContain('ORCA_OMP_FRESH_CONFIG')
expect(plan?.launchCommand).toContain('test -f "$ORCA_OMP_FRESH_CONFIG"')
})
})
+29 -6
View File
@@ -23,14 +23,14 @@ const VALUE_FLAGS = new Set([
const SWITCH_FLAGS = new Set(['--no-extensions', '--no-skills', '--no-prompt-templates'])
/** Apply fresh intent to one launch command, never the saved resume configuration. */
export function withFreshOmpLaunch(command: string, shell: AgentStartupShell): string {
export function withFreshOmpLaunch(command: string, shell: AgentStartupShell, suffix = ''): string {
const parsed = tokenizeStartupCommand(command, shell)
if (!parsed.ok) {
return command
return command + suffix
}
const executable = parsed.tokens[0]?.split(/[\\/]/).at(-1)?.toLowerCase()
if (!['omp', 'omp.exe', 'omp.cmd', 'omp.bat', 'omp.sh', 'omp.js'].includes(executable ?? '')) {
return command
return command + suffix
}
let index = parsed.tokens[1] === 'launch' ? 2 : 1
for (; index < parsed.tokens.length; index++) {
@@ -39,10 +39,10 @@ export function withFreshOmpLaunch(command: string, shell: AgentStartupShell): s
const flag = equals === -1 ? token : token.slice(0, equals)
if (VALUE_FLAGS.has(flag)) {
if (equals === -1 && ++index >= parsed.tokens.length) {
return command
return command + suffix
}
} else if (!SWITCH_FLAGS.has(token)) {
return command
return command + suffix
}
}
const path =
@@ -51,5 +51,28 @@ export function withFreshOmpLaunch(command: string, shell: AgentStartupShell): s
: shell === 'powershell'
? `"$env:${ORCA_OMP_FRESH_CONFIG_ENV}"`
: `"$${ORCA_OMP_FRESH_CONFIG_ENV}"`
return `${command} --config ${path}`
const launch = `${command} --config ${path}${suffix}`
const error =
'Orca: fresh OMP settings are unavailable on this host. Restart the terminal after updating Orca.'
if (shell === 'powershell') {
return `if (${path} -and (Test-Path -LiteralPath ${path} -PathType Leaf)) { ${launch} } else { $global:LASTEXITCODE = 1; Write-Error '${error}' }`
}
if (shell === 'cmd') {
return `if exist ${path} (if not exist "${path.slice(1, -1)}\\*" (${launch}) else (echo ${error} 1>&2 & verify invalid 2>nul)) else (echo ${error} 1>&2 & verify invalid 2>nul)`
}
const available = `printenv ${ORCA_OMP_FRESH_CONFIG_ENV} >/dev/null && test -f ${path}`
// && is supported by bash, zsh, and fish; invoking directly preserves shell aliases/functions.
return `${available} || printf '%s\\n' '${error}' >&2; ${available} && ${launch}`
}
/** Recognize only the host-settings guards generated by this module. */
export function isFreshOmpLaunchCommand(command: string | undefined): boolean {
return Boolean(
command &&
[
`printenv ${ORCA_OMP_FRESH_CONFIG_ENV} >/dev/null && test -f "$${ORCA_OMP_FRESH_CONFIG_ENV}"`,
`if ("$env:${ORCA_OMP_FRESH_CONFIG_ENV}" -and (Test-Path -LiteralPath "$env:${ORCA_OMP_FRESH_CONFIG_ENV}" -PathType Leaf))`,
`if exist "%${ORCA_OMP_FRESH_CONFIG_ENV}%" (if not exist "%${ORCA_OMP_FRESH_CONFIG_ENV}%\\*"`
].some((prefix) => command.startsWith(prefix))
)
}
+4
View File
@@ -1,3 +1,4 @@
import { isFreshOmpLaunchCommand } from './omp-fresh-launch'
import { TUI_AGENT_CONFIG } from './tui-agent-config'
import { getCommandTokenPathBasename, getFirstCommandToken } from './command-token-scanner'
@@ -49,6 +50,9 @@ const PRIME_AGENT_LAUNCH_BINARY = getLaunchBinary(TUI_AGENT_CONFIG['prime-agent'
export function detectExplicitPiAgentKindFromCommand(
command: string | undefined
): PiAgentKind | null {
if (isFreshOmpLaunchCommand(command)) {
return 'omp'
}
const binary = getLaunchBinary(command ?? '')
if (binary === OMP_LAUNCH_BINARY) {
return 'omp'
+1 -1
View File
@@ -666,7 +666,7 @@ describe('tui agent startup plans', () => {
expect(plan).not.toBeNull()
expect(plan?.env).toEqual({ ORCA_OMP_PREFILL: 'fix the omp regression' })
expect(plan?.expectedProcess).toBe('omp')
expect(plan?.launchCommand).toBe(
expect(plan?.launchCommand).toContain(
`omp --config "$ORCA_OMP_FRESH_CONFIG"; command test -n "$fish_pid" && set --erase -g ORCA_OMP_PREFILL; command test -z "$fish_pid" && unset ORCA_OMP_PREFILL; true`
)
})
+6 -7
View File
@@ -72,9 +72,7 @@ export function buildAgentStartupPlan(args: {
return null
}
const launchCommand =
agent === 'omp' && !args.isRemote
? withFreshOmpLaunch(baseCommand.command, shell)
: baseCommand.command
agent === 'omp' ? withFreshOmpLaunch(baseCommand.command, shell) : baseCommand.command
const launchConfig = buildSleepingAgentLaunchConfig({
...args,
// Why: picker flags are a one-time launch choice; a resumed provider
@@ -103,7 +101,10 @@ export function buildAgentStartupPlan(args: {
const promptSeparator = config.argvPromptSeparator ? ` ${config.argvPromptSeparator}` : ''
return {
agent,
launchCommand: `${launchCommand}${promptSeparator} ${quotedPrompt}`,
launchCommand:
agent === 'omp'
? withFreshOmpLaunch(baseCommand.command, shell, `${promptSeparator} ${quotedPrompt}`)
: `${launchCommand}${promptSeparator} ${quotedPrompt}`,
expectedProcess: config.expectedProcess,
followupPrompt: null,
launchConfig,
@@ -228,9 +229,7 @@ export function buildAgentDraftLaunchPlan(args: {
return null
}
const launchCommand =
agent === 'omp' && !args.isRemote
? withFreshOmpLaunch(baseCommand.command, shell)
: baseCommand.command
agent === 'omp' ? withFreshOmpLaunch(baseCommand.command, shell) : baseCommand.command
const launchConfig = buildSleepingAgentLaunchConfig({
...args,
// Why: see the new-session path above — resume must not replay picker flags.
@@ -0,0 +1,77 @@
import { mkdtempSync, writeFileSync, readFileSync, rmSync, mkdirSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import assert from 'node:assert/strict'
import { runProcess } from '../../src/shared/child-process/run-process'
import { withFreshOmpLaunch } from '../../src/shared/omp-fresh-launch'
if (process.platform !== 'win32') {
throw new Error('Run this smoke on a Windows host')
}
const root = mkdtempSync(join(tmpdir(), 'orca-fresh-windows-'))
const config = join(root, 'fresh settings.yml')
const capture = join(root, 'calls')
const results = []
try {
writeFileSync(join(root, 'omp.cmd'), '@echo off\r\necho %*>>"%CAPTURE%"\r\nexit /b 17\r\n')
for (const shell of ['cmd', 'powershell'] as const) {
for (const state of ['present', 'missing', 'unset', 'directory']) {
rmSync(config, { force: true, recursive: true })
rmSync(capture, { force: true })
if (state === 'present') {
writeFileSync(config, 'autoResume: false\n')
}
if (state === 'directory') {
mkdirSync(config)
}
const env: NodeJS.ProcessEnv = {
...process.env,
ORCA_BACKGROUND_LAUNCH: '1',
ORCA_OMP_FRESH_CONFIG: config,
CAPTURE: capture
}
if (state === 'unset') {
delete env.ORCA_OMP_FRESH_CONFIG
}
const command = withFreshOmpLaunch('omp', shell, ' "task with spaces"')
const runner = join(root, 'run.cmd')
writeFileSync(runner, `@echo off\r\n${command}\r\n`)
const result = await runProcess({
program: shell === 'cmd' ? runner : 'powershell.exe',
args:
shell === 'cmd'
? []
: [
'-NoProfile',
'-NonInteractive',
'-Command',
`function omp { $args | ConvertTo-Json -Compress | Add-Content -LiteralPath $env:CAPTURE; $global:LASTEXITCODE = 17 }; ${
command
}; exit $global:LASTEXITCODE`
],
cwd: root,
env
})
assert.equal(
result.code,
state === 'present' ? 17 : 1,
JSON.stringify({ shell, state, ...result })
)
if (state === 'present') {
const calls = readFileSync(capture, 'utf8').trim().split(/\r?\n/)
assert.equal(calls.length, 1)
assert.ok(calls[0].includes('--config'))
assert.ok(calls[0].includes('task with spaces'))
} else {
assert.ok(
result.stderr.includes('fresh OMP settings are unavailable'),
JSON.stringify(result)
)
assert.throws(() => readFileSync(capture))
}
results.push({ shell, state, code: result.code, passed: true })
}
}
console.log(JSON.stringify(results))
} finally {
rmSync(root, { recursive: true, force: true })
}