fix(omp): start new tasks without auto-resuming old sessions (#20622)

* wip(omp): prove fresh settings overlay without redirecting storage

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

* fix(omp): preserve unmodelled shell launch commands

* test(omp): consolidate shell fixture path import

* preserve fresh OMP launch status

* test: cover preserved OMP launch status

* fix: recognize wrapped fresh OMP launches

* chore(ci): refresh validation against fixed main baseline

* fix(omp): recognize generated fresh launch guards across shells

* fix(omp): preserve draft status and clear prefill across Unix shells

* fix(omp): run cmd draft cleanup after either guard branch

* fix(omp): launch drafts safely with nounset enabled

* fix(omp): select draft shell without parser diagnostics

* test(omp): await relay environment augmentation
This commit is contained in:
Neil
2026-09-19 01:42:14 -07:00
committed by GitHub
parent 14cddfeda7
commit 605a4ef868
26 changed files with 854 additions and 23 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
@@ -275,6 +275,8 @@ describe('PiTitlebarExtensionService', () => {
const content = 'agent.db credentials'
expect(env.PI_CODING_AGENT_DIR).toBeUndefined()
expect(readFileSync(env.ORCA_OMP_FRESH_CONFIG, 'utf8')).toBe('autoResume: false\n')
expect(env.ORCA_OMP_FRESH_CONFIG.startsWith(userDataDir)).toBe(true)
expect(env.ORCA_OMP_SOURCE_AGENT_DIR).toBe(piHome)
expect(env.ORCA_OMP_STATUS_EXTENSION).toBe(join(piHome, 'extensions', 'orca-agent-status.ts'))
expect(existsSync(sourcePath)).toBe(false)
+17 -6
View File
@@ -1,3 +1,4 @@
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'
@@ -174,16 +175,24 @@ 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 sourceAgentDir = existingAgentDir || getDefaultPiAgentDir(kind, options?.configDirName)
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') {
try {
this.safeRemoveOverlay(this.getPtyOverlayDir(ptyId, kind), kind)
@@ -203,7 +212,9 @@ export class PiTitlebarExtensionService {
if (kind === 'omp') {
const statusSource = withOrcaManagedExtensionMarker(getPiAgentStatusExtensionSource(kind))
const statusExtensionPath = this.writeOmpFallbackStatusExtension(statusSource)
return statusExtensionPath ? { ORCA_OMP_STATUS_EXTENSION: statusExtensionPath } : {}
return statusExtensionPath
? { ...freshConfigEnv, ORCA_OMP_STATUS_EXTENSION: statusExtensionPath }
: freshConfigEnv
}
return {}
}
@@ -213,7 +224,7 @@ export class PiTitlebarExtensionService {
}
const installed = this.installManagedExtensions(sourceAgentDir, kind)
const env: Record<string, string> = {}
const env: Record<string, string> = { ...freshConfigEnv }
if (kind === 'omp') {
env.ORCA_OMP_SOURCE_AGENT_DIR = installed.sourceAgentDir
if (installed.statusExtensionPath) {
+2
View File
@@ -63,6 +63,7 @@ describe('addOrcaWslInteropEnv', () => {
ORCA_USER_DATA_PATH: 'C:\\Users\\jin\\AppData\\Roaming\\Orca',
ORCA_CLI_COMMAND: 'orca-ide',
ORCA_CODEX_LAUNCH_PREFLIGHT: 'C:\\Program Files\\Orca\\resources\\bin\\orca.exe',
ORCA_OMP_FRESH_CONFIG: 'C:\\Orca\\fresh-session.yml',
ORCA_OMP_STATUS_EXTENSION: 'C:\\Users\\jin\\.omp\\agent\\extensions\\orca-agent-status.ts',
ORCA_PRIME_AGENT_STATUS_EXTENSION: 'C:\\stale\\orca-agent-status.ts',
ORCA_PANE_KEY: 'tab-1:leaf-1',
@@ -87,6 +88,7 @@ describe('addOrcaWslInteropEnv', () => {
expect(env.WSLENV).toContain('ORCA_CLI_COMMAND/u')
expect(env.WSLENV).toContain('ORCA_CODEX_LAUNCH_PREFLIGHT/p')
expect(env.WSLENV).toContain('ORCA_OMP_STATUS_EXTENSION/p')
expect(env.WSLENV).toContain('ORCA_OMP_FRESH_CONFIG/p')
expect(env.WSLENV).not.toContain('ORCA_PRIME_AGENT_STATUS_EXTENSION')
expect(env.WSLENV).toContain('ORCA_PANE_KEY/u')
expect(env.WSLENV).toContain('ORCA_TAB_ID/u')
+1 -1
View File
@@ -99,8 +99,8 @@ export function addOrcaWslInteropEnv(env: Record<string, string>): void {
'ORCA_WSL_HOOK_INSTANCE/u',
'ORCA_OMP_SOURCE_AGENT_DIR/p',
'ORCA_OMP_STATUS_EXTENSION/p',
// A protocol name, never a path; in-guest agents read it to pick an image encoder.
`${ORCA_IMAGE_PROTOCOL_ENV}/u`,
'ORCA_OMP_FRESH_CONFIG/p',
...worktreeSetupWslenvEntries(env)
]
applyWslenvPassthrough(env, passthroughEntries)
@@ -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 = await 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', {
@@ -1,4 +1,7 @@
import { describe, expect, it } from 'vitest'
import { withFreshOmpLaunch } from './omp-fresh-launch'
import { buildAgentDraftLaunchPlan } from './tui-agent-startup'
import { detectExplicitPiAgentKindFromCommand } from './pi-agent-kind'
import {
isAgentForegroundWrapperProcess,
isExpectedAgentProcess,
@@ -16,6 +19,42 @@ describe('agent process recognition', () => {
expect(isRecognizedAgentType('codex-aarch64-ap')).toBe(true)
})
it.each(['posix', 'powershell', 'cmd'] as const)(
'recognizes fresh OMP commands and drafts in %s',
(shell) => {
for (const command of ['omp', 'omp launch', 'omp --model example']) {
const generated = withFreshOmpLaunch(command, shell)
expect(recognizeAgentProcessFromCommandLine(generated)).toEqual({
agent: 'omp',
processName: 'omp'
})
expect(detectExplicitPiAgentKindFromCommand(generated)).toBe('omp')
}
const draft = buildAgentDraftLaunchPlan({
agent: 'omp',
draft: 'task',
cmdOverrides: {},
platform: 'linux',
shell
})
expect(recognizeAgentProcessFromCommandLine(draft?.launchCommand)).toEqual({
agent: 'omp',
processName: 'omp'
})
expect(detectExplicitPiAgentKindFromCommand(draft?.launchCommand)).toBe('omp')
}
)
it('does not classify embedded fresh-launch text as an agent command', () => {
for (const command of [
`echo 'omp --config "$ORCA_OMP_FRESH_CONFIG"'`,
`echo '${withFreshOmpLaunch('omp', 'posix')}'`
]) {
expect(recognizeAgentProcessFromCommandLine(command)).toBeNull()
expect(detectExplicitPiAgentKindFromCommand(command)).toBeNull()
}
})
it('recognizes the OpenClaude foreground process', () => {
expect(recognizeAgentProcess('/usr/local/bin/openclaude')).toEqual({
agent: 'openclaude',
+4
View File
@@ -4,6 +4,7 @@ import type { AgentType } from './agent-status-types'
import type { TuiAgent } from './tui-agent'
import { filterHeadlessOneShotAgentCommand } from './agent-headless-command'
import { getFirstCommandToken } from './command-token-scanner'
import { isFreshOmpLaunchCommand } from './omp-fresh-launch'
export type RecognizedAgentProcess = { agent: TuiAgent; processName: string }
@@ -286,6 +287,9 @@ export function recognizeAgentProcessFromCommandLine(
if (!commandLine) {
return null
}
if (isFreshOmpLaunchCommand(commandLine)) {
return recognizedAgentForProcess('omp')
}
const keep = options?.includeHeadlessOneShot === true
const tokens = tokenizeCommandLine(commandLine)
const firstNormalized = normalizeProcessName(tokens[0])
+72
View File
@@ -0,0 +1,72 @@
import { existsSync } from 'node:fs'
import { mkdtemp, writeFile, readFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { delimiter, join } from 'node:path'
import { expect, it } from 'vitest'
import { runProcess } from './child-process/run-process'
import { buildAgentDraftLaunchPlan } from './tui-agent-startup'
const shells = ['sh', 'bash', 'zsh', 'dash', 'ksh', 'fish'].filter(
(shell) =>
process.platform !== 'win32' &&
(process.env.PATH ?? '')
.split(delimiter)
.some((directory) => existsSync(join(directory, shell)))
)
it.each(
shells.flatMap((shell) =>
shell === 'fish'
? [{ shell, nounset: false }]
: [false, true].map((nounset) => ({ shell, nounset }))
)
)(
'clears the draft and preserves status in $shell (nounset=$nounset)',
async ({ shell, nounset }) => {
const root = await mkdtemp(join(tmpdir(), 'orca-omp-draft-'))
try {
const config = join(root, 'fresh settings.yml')
const calls = join(root, 'calls')
await writeFile(config, 'autoResume: false\n')
const plan = buildAgentDraftLaunchPlan({
agent: 'omp',
draft: 'task with spaces',
cmdOverrides: {},
platform: 'linux'
})
if (!plan) {
throw new Error('Expected draft plan')
}
const define =
shell === 'fish'
? 'function omp; printf "%s\\n" "$ORCA_OMP_PREFILL" >> "$CAPTURE"; printf agent-stderr >&2; return 17; end; '
: 'omp() { printf "%s\\n" "$ORCA_OMP_PREFILL" >> "$CAPTURE"; printf agent-stderr >&2; return 17; }; '
const observe =
shell === 'fish'
? '; set -l result $status; set -q ORCA_OMP_PREFILL; and exit 91; exit $result'
: '; result=$?; test -z "${ORCA_OMP_PREFILL+x}" || exit 91; exit "$result"'
for (const present of [true, false]) {
if (!present) {
await rm(config)
}
const result = await runProcess({
program: shell,
args: ['-c', (nounset ? 'set -u; ' : '') + define + plan.launchCommand + observe],
cwd: root,
env: { ...process.env, ...plan.env, ORCA_OMP_FRESH_CONFIG: config, CAPTURE: calls }
})
expect(result.code, result.stderr).toBe(present ? 17 : 1)
expect(result.stderr).toContain(
present ? 'agent-stderr' : 'fresh OMP settings are unavailable'
)
if (present) {
expect(result.stderr).toBe('agent-stderr')
}
expect(result.stderr).not.toContain('command substitutions not allowed')
expect(await readFile(calls, 'utf8')).toBe('task with spaces\n')
}
} finally {
await rm(root, { recursive: true, force: true })
}
}
)
@@ -0,0 +1,76 @@
import { mkdtemp, writeFile, readFile, rm, mkdir } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { expect, it } from 'vitest'
import { runProcess } from './child-process/run-process'
import { buildAgentDraftLaunchPlan } from './tui-agent-startup'
it.skipIf(process.platform !== 'win32').each(['cmd', 'powershell'] as const)(
'clears the draft and preserves status in native %s',
async (shell) => {
const root = await mkdtemp(join(tmpdir(), 'orca-omp-draft-'))
try {
const config = join(root, 'fresh (%ORCA_EXPANSION_PROBE%) & settings!.yml')
const calls = join(root, 'calls')
await writeFile(
join(root, 'omp.cmd'),
'@echo off\r\necho %ORCA_OMP_PREFILL%>>"%CAPTURE%"\r\nexit /b 17\r\n'
)
const plan = buildAgentDraftLaunchPlan({
agent: 'omp',
draft: 'task with spaces',
cmdOverrides: {},
platform: 'win32',
shell
})
if (!plan) {
throw new Error('Expected draft plan')
}
for (const state of ['present', 'missing', 'unset', 'directory']) {
await rm(config, { force: true, recursive: true })
await rm(calls, { force: true })
if (state === 'present') {
await writeFile(config, 'autoResume: false\n')
}
if (state === 'directory') {
await mkdir(config)
}
const env = {
...process.env,
...plan.env,
ORCA_OMP_FRESH_CONFIG: state === 'unset' ? undefined : config,
CAPTURE: calls,
ORCA_EXPANSION_PROBE: 'unexpected'
}
const result =
shell === 'cmd'
? await runProcess({
program: 'cmd.exe',
args: ['/d', '/q'],
cwd: root,
env,
input: `${plan.launchCommand}\r\nset "orca_result=%errorlevel%"\r\nif defined ORCA_OMP_PREFILL exit 91\r\nexit %orca_result%\r\n`
})
: await runProcess({
program: 'powershell.exe',
args: [
'-NoProfile',
'-NonInteractive',
'-Command',
`function omp { Add-Content -LiteralPath $env:CAPTURE -Value $env:ORCA_OMP_PREFILL; $global:LASTEXITCODE = 17 }; ${plan.launchCommand}; $result = $LASTEXITCODE; if (Test-Path Env:ORCA_OMP_PREFILL) { exit 91 }; exit $result`
],
cwd: root,
env
})
expect(result.code, JSON.stringify({ state, ...result })).toBe(state === 'present' ? 17 : 1)
if (state === 'present') {
expect((await readFile(calls, 'utf8')).trim()).toBe('task with spaces')
} else {
await expect(readFile(calls)).rejects.toMatchObject({ code: 'ENOENT' })
}
}
} finally {
await rm(root, { recursive: true, force: true })
}
}
)
+22
View File
@@ -0,0 +1,22 @@
import {
clearEnvCommand,
commandSeparator,
quoteStartupArg,
type AgentStartupShell
} from './tui-agent-startup-shell'
const FUNCTION_NAME = '__orca_omp_draft'
export const OMP_DRAFT_LAUNCH_PREFIX = `eval 'builtin --query set' 2>/dev/null && eval 'function ${FUNCTION_NAME}; `
/** Clear the calling shell's prefill without replacing the agent's exit status. */
export function withOmpDraftCleanup(command: string, shell: AgentStartupShell): string {
if (shell !== 'posix') {
// cmd otherwise binds the cleanup to the guard's else branch.
const launch = shell === 'cmd' ? `( ${command} )` : command
return `${launch}${commandSeparator(shell)}${clearEnvCommand('ORCA_OMP_PREFILL', shell)}`
}
const fish = `function ${FUNCTION_NAME}; ${command}; set -l __orca_status $status; set -e -g ORCA_OMP_PREFILL; return $__orca_status; end`
const posix = `${FUNCTION_NAME}() { ${command}; set -- "$?"; unset ORCA_OMP_PREFILL; return "$1"; }`
// Fish supports builtin --query; other shells reject it without parsing the wrong definition.
return `eval 'builtin --query set' 2>/dev/null && eval ${quoteStartupArg(fish, 'posix')} || eval ${quoteStartupArg(posix, 'posix')}; ${FUNCTION_NAME}`
}
+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 ''
}
}
+73
View File
@@ -0,0 +1,73 @@
import { existsSync } from 'node:fs'
import { mkdtemp, writeFile, readFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { delimiter, 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' })
}
)
+68
View File
@@ -0,0 +1,68 @@
import { describe, expect, it } from 'vitest'
import { withFreshOmpLaunch } from './omp-fresh-launch'
import { buildAgentStartupPlan } from './tui-agent-startup'
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')).toContain(
`${command} --config "$ORCA_OMP_FRESH_CONFIG"`
)
}
)
it.each([
'omp --resume id',
'omp -r id',
'omp --continue',
'omp -c',
'omp --session-dir /custom',
'omp --no-session',
'omp --fork id',
'omp models',
'omp config',
'omp wt',
'omp --help',
'omp --unknown foo',
'omp --model',
'omp -- hello',
'echo omp',
'omp && echo hi',
'omp --model foo;',
'omp --model $(preferred-model)',
'omp --model `preferred-model`'
])('preserves %s', (command) => {
expect(withFreshOmpLaunch(command, 'posix')).toBe(command)
})
it.each(['cmd', 'powershell'] as const)('preserves compound values in %s', (shell) => {
const command = 'omp --model foo&'
expect(withFreshOmpLaunch(command, shell)).toBe(command)
})
it('quotes the host config path for each Windows shell', () => {
expect(withFreshOmpLaunch('omp', 'powershell')).toContain(
'omp --config "$env: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({
agent: 'omp',
prompt: 'new task',
cmdOverrides: {},
platform: 'linux'
})
expect(plan?.launchCommand).toContain('--config "$ORCA_OMP_FRESH_CONFIG"')
expect(JSON.stringify(plan?.launchConfig)).not.toContain('ORCA_OMP_FRESH_CONFIG')
expect(plan?.env).toBeUndefined()
})
it('requires host-owned configuration for fresh SSH launches', () => {
const plan = buildAgentStartupPlan({
agent: 'omp',
prompt: 'new task',
cmdOverrides: {},
platform: 'linux',
isRemote: true
})
expect(plan?.launchCommand).toContain('test -f "$ORCA_OMP_FRESH_CONFIG"')
})
})
+83
View File
@@ -0,0 +1,83 @@
import { OMP_DRAFT_LAUNCH_PREFIX } from './omp-draft-launch'
import { tokenizeStartupCommand, type AgentStartupShell } from './tui-agent-startup-shell'
export const ORCA_OMP_FRESH_CONFIG_ENV = 'ORCA_OMP_FRESH_CONFIG'
export const OMP_FRESH_CONFIG_FILENAME = 'fresh-session.yml'
export const OMP_FRESH_CONFIG_SOURCE = 'autoResume: false\n'
// Unknown flags may consume values or select a subcommand; leave those commands intact.
const VALUE_FLAGS = new Set([
'--model',
'--provider',
'--thinking',
'--config',
'--profile',
'--extension',
'-e',
'--system-prompt',
'--append-system-prompt',
'--tools',
'--skill',
'--theme',
'--api-key'
])
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, suffix = ''): string {
const parsed = tokenizeStartupCommand(command, shell)
if (!parsed.ok || parsed.spans.some((span) => span.divergesFromShell)) {
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 + suffix
}
let index = parsed.tokens[1] === 'launch' ? 2 : 1
for (; index < parsed.tokens.length; index++) {
const token = parsed.tokens[index]
const equals = token.indexOf('=')
const flag = equals === -1 ? token : token.slice(0, equals)
if (VALUE_FLAGS.has(flag)) {
if (equals === -1 && ++index >= parsed.tokens.length) {
return command + suffix
}
} else if (!SWITCH_FLAGS.has(token)) {
return command + suffix
}
}
const path =
shell === 'cmd'
? `"%${ORCA_OMP_FRESH_CONFIG_ENV}%"`
: shell === 'powershell'
? `"$env:${ORCA_OMP_FRESH_CONFIG_ENV}"`
: `"$${ORCA_OMP_FRESH_CONFIG_ENV}"`
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 {
if (command?.startsWith(OMP_DRAFT_LAUNCH_PREFIX)) {
return true
}
const guarded = command?.startsWith('( ') ? command.slice(2) : command
return Boolean(
guarded &&
[
`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) => guarded.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 -3
View File
@@ -666,9 +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(
`omp; command test -n "$fish_pid" && set --erase -g ORCA_OMP_PREFILL; command test -z "$fish_pid" && unset ORCA_OMP_PREFILL; true`
)
expect(plan?.launchConfig.agentCommand).toBe('omp')
})
it('returns null for oversized Windows flag drafts so callers paste after ready', () => {
+20 -8
View File
@@ -1,3 +1,5 @@
import { withFreshOmpLaunch, isFreshOmpLaunchCommand } from './omp-fresh-launch'
import { withOmpDraftCleanup } from './omp-draft-launch'
import { isShellProcess } from './agent-detection'
import type { SleepingAgentLaunchConfig } from './agent-session-resume'
import {
@@ -70,6 +72,8 @@ export function buildAgentStartupPlan(args: {
if (!baseCommand.ok) {
return null
}
const launchCommand =
agent === 'omp' ? withFreshOmpLaunch(baseCommand.command, shell) : baseCommand.command
const launchConfig = buildSleepingAgentLaunchConfig({
...args,
// Why: picker flags are a one-time launch choice; a resumed provider
@@ -83,7 +87,7 @@ export function buildAgentStartupPlan(args: {
}
return {
agent,
launchCommand: baseCommand.command,
launchCommand,
expectedProcess: config.expectedProcess,
followupPrompt: null,
launchConfig,
@@ -98,7 +102,10 @@ export function buildAgentStartupPlan(args: {
const promptSeparator = config.argvPromptSeparator ? ` ${config.argvPromptSeparator}` : ''
return {
agent,
launchCommand: `${baseCommand.command}${promptSeparator} ${quotedPrompt}`,
launchCommand:
agent === 'omp'
? withFreshOmpLaunch(baseCommand.command, shell, `${promptSeparator} ${quotedPrompt}`)
: `${launchCommand}${promptSeparator} ${quotedPrompt}`,
expectedProcess: config.expectedProcess,
followupPrompt: null,
launchConfig,
@@ -111,7 +118,7 @@ export function buildAgentStartupPlan(args: {
if (config.promptInjectionMode === 'flag-prompt') {
return {
agent,
launchCommand: `${baseCommand.command} --prompt ${quotedPrompt}`,
launchCommand: `${launchCommand} --prompt ${quotedPrompt}`,
expectedProcess: config.expectedProcess,
followupPrompt: null,
launchConfig,
@@ -149,7 +156,7 @@ export function buildAgentStartupPlan(args: {
if (config.promptInjectionMode === 'flag-prompt-interactive') {
return {
agent,
launchCommand: `${baseCommand.command} --prompt-interactive ${quotedPrompt}`,
launchCommand: `${launchCommand} --prompt-interactive ${quotedPrompt}`,
expectedProcess: config.expectedProcess,
followupPrompt: null,
launchConfig,
@@ -161,7 +168,7 @@ export function buildAgentStartupPlan(args: {
if (config.promptInjectionMode === 'flag-interactive') {
return {
agent,
launchCommand: `${baseCommand.command} -i ${quotedPrompt}`,
launchCommand: `${launchCommand} -i ${quotedPrompt}`,
expectedProcess: config.expectedProcess,
followupPrompt: null,
launchConfig,
@@ -172,7 +179,7 @@ export function buildAgentStartupPlan(args: {
return {
agent,
launchCommand: baseCommand.command,
launchCommand,
expectedProcess: config.expectedProcess,
followupPrompt: trimmedPrompt,
launchConfig,
@@ -222,6 +229,8 @@ export function buildAgentDraftLaunchPlan(args: {
if (!baseCommand.ok) {
return null
}
const launchCommand =
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.
@@ -232,7 +241,7 @@ export function buildAgentDraftLaunchPlan(args: {
const quoted = quoteStartupArg(trimmed, shell)
plan = {
agent,
launchCommand: `${baseCommand.command} ${config.draftPromptFlag} ${quoted}`,
launchCommand: `${launchCommand} ${config.draftPromptFlag} ${quoted}`,
expectedProcess: config.expectedProcess,
launchConfig,
...appliedSessionOptionProps(baseCommand.appliedSessionOptions),
@@ -244,7 +253,10 @@ export function buildAgentDraftLaunchPlan(args: {
const clearVar = clearEnvCommand(config.draftPromptEnvVar, shell)
plan = {
agent,
launchCommand: `${baseCommand.command}${commandSeparator(shell)}${clearVar}`,
launchCommand:
agent === 'omp' && isFreshOmpLaunchCommand(launchCommand)
? withOmpDraftCleanup(launchCommand, shell)
: `${launchCommand}${commandSeparator(shell)}${clearVar}`,
expectedProcess: config.expectedProcess,
launchConfig,
...appliedSessionOptionProps(baseCommand.appliedSessionOptions),
@@ -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 })
}
@@ -0,0 +1,75 @@
// Bun, with a read-only OMP source checkout as argv[2]. No model requests.
import assert from 'node:assert/strict'
import { mkdtemp, mkdir, writeFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
import { pathToFileURL } from 'node:url'
import { OMP_FRESH_CONFIG_SOURCE } from '../../src/shared/omp-fresh-launch.ts'
assert.ok(process.argv[2], 'Pass a read-only OMP checkout path')
const scratch = await mkdtemp(join(tmpdir(), 'orca-omp-fresh-proof-'))
process.env.HOME = join(scratch, 'home')
process.env.USERPROFILE = process.env.HOME
process.env.XDG_CONFIG_HOME = join(scratch, 'xdg-config')
process.env.XDG_DATA_HOME = join(scratch, 'xdg-data')
process.env.XDG_STATE_HOME = join(scratch, 'xdg-state')
process.env.OMP_CODING_AGENT_DIR = join(scratch, 'agent')
delete process.env.PI_CONFIG_FILES
const source = (path) =>
pathToFileURL(join(resolve(process.argv[2]), 'packages/coding-agent/src', path)).href
const managers = []
try {
await mkdir(process.env.HOME, { recursive: true })
const { SessionManager } = await import(source('session/session-manager.ts'))
const { Settings, resetSettingsForTest } = await import(source('config/settings.ts'))
const { createSessionManager } = await import(source('main.ts'))
const cwd = join(scratch, 'project')
await mkdir(cwd)
const previous = SessionManager.create(cwd)
managers.push(previous)
previous.appendMessage({ role: 'user', content: 'previous task', timestamp: Date.now() })
await previous.ensureOnDisk()
await previous.flush()
const config = join(scratch, 'fresh.yml')
const userConfig = join(scratch, 'user.yml')
await writeFile(config, OMP_FRESH_CONFIG_SOURCE)
await writeFile(userConfig, 'autoResume: true\n')
const settings = await Settings.init({ cwd, configFiles: [userConfig] })
const resumed = await createSessionManager({}, cwd, settings)
managers.push(resumed)
assert.equal(resumed.getSessionId(), previous.getSessionId())
resetSettingsForTest()
const freshSettings = await Settings.init({ cwd, configFiles: [userConfig, config] })
assert.equal(freshSettings.get('autoResume'), false)
assert.equal(settings.get('autoResume'), true)
const defaultSelection = await createSessionManager({}, cwd, freshSettings)
assert.equal(defaultSelection, undefined, 'SDK creates a fresh session after undefined selection')
const fresh = SessionManager.create(cwd)
managers.push(fresh)
assert.notEqual(fresh.getSessionId(), previous.getSessionId())
assert.equal(fresh.getSessionDir(), previous.getSessionDir())
const explicit = await createSessionManager(
{ resume: previous.getSessionFile() },
cwd,
freshSettings
)
managers.push(explicit)
assert.equal(explicit.getSessionId(), previous.getSessionId())
console.log(
JSON.stringify({
platform: process.platform,
autoResumeReproduced: true,
freshSelected: true,
storageDirectoryPreserved: true,
explicitResumePreserved: true,
modelCalls: 0,
scope:
'Actual OMP Settings overlays, persistent SessionManager and createSessionManager; no rendered UI'
})
)
} finally {
for (const manager of managers) {
await manager?.close()
}
await rm(scratch, { recursive: true, force: true })
}