Files
orca/src/main/runtime/cli-terminal-create-host-session-binding.test.ts
T
Neil 17ffbf3b31 fix(runtime): answer startup terminal queries for background-created terminals (#22384)
* fix(runtime): answer startup terminal queries for background-created terminals

A runtime-created terminal (orchestration worker-start, `orca terminal
create`) has no renderer pane until the user opens its tab. Main only
answers terminal queries for PTYs the renderer marked hidden, so on a
fresh app sitting on the landing screen nothing answered the agent's
startup cursor-position query. Muse waits ~2s per unanswered CPR and then
exits 0 with no output, which surfaced as `agent_readiness: timeout`.

Background runtime spawns now carry initiallyHidden, mirroring the
renderer's hidden-at-spawn path: fresh daemon sessions are marked before
byte zero, the committed id is marked and paced as backgrounded, and the
mark is released on failure, reattach, or adoption. A pane that later
mounts visible unmarks and restores from the model snapshot as before.

* fix(runtime): keep background PTYs hidden and paced across renderer reloads

A runtime background spawn has no renderer pane to report visibility or
re-mark it hidden, so it synced as foregrounded (no backpressure thinning)
and a reload/crash gate reset cleared its hidden mark, leaving startup
queries unanswered. Track runtime-owned hidden marks: they survive
renderer-scoped resets, count as known-hidden for backgrounded pacing
until a visible report, and are released by a renderer unmark or PTY
teardown.

* fix(runtime): don't re-hide a background PTY whose view mounted visible during spawn
2026-09-22 23:00:11 -07:00

73 lines
2.5 KiB
TypeScript

/** A host-initiated create has no renderer session writer, so only its own
* durable binding and runtime ownership keep graph sync from pruning the tab
* out from under a live agent — including when a window is attached. */
import { describe, expect, it, vi } from 'vitest'
import { OrcaRuntimeService } from './orca-runtime'
vi.mock('electron', () => ({
BrowserWindow: { fromId: vi.fn(() => ({ isDestroyed: () => false })) },
webContents: { fromId: vi.fn(() => null) },
ipcMain: { on: vi.fn(), removeListener: vi.fn() },
app: { getPath: vi.fn(() => '/tmp') }
}))
function createRuntimeWithAttachedWindow(): {
runtime: OrcaRuntimeService
spawn: ReturnType<typeof vi.fn>
} {
const runtime = new OrcaRuntimeService()
vi.spyOn(
runtime as unknown as {
resolveTerminalWorkspaceLaunchScope: (selector: string) => Promise<unknown>
},
'resolveTerminalWorkspaceLaunchScope'
).mockResolvedValue({
id: 'repo-1::/tmp/wt-cli',
path: '/tmp/wt-cli',
connectionId: null,
repo: null,
folderWorkspace: null
})
const spawn = vi.fn().mockResolvedValue({ id: 'repo-1::/tmp/wt-cli@@a1b2c3d4' })
runtime.setPtyController({
spawn,
write: () => true,
kill: () => true,
getForegroundProcess: async () => null
} as never)
runtime.attachWindow(1)
return { runtime, spawn }
}
describe('host-initiated terminal creation under an attached window', () => {
it('persists the host session binding at spawn', async () => {
const { runtime, spawn } = createRuntimeWithAttachedWindow()
await runtime.createTerminal('id:repo-1::/tmp/wt-cli', {})
expect(spawn).toHaveBeenCalledWith(expect.objectContaining({ persistHostSessionBinding: true }))
})
it('spawns hidden so main answers startup queries before any pane mounts', async () => {
const { runtime, spawn } = createRuntimeWithAttachedWindow()
await runtime.createTerminal('id:repo-1::/tmp/wt-cli', {})
expect(spawn).toHaveBeenCalledWith(expect.objectContaining({ initiallyHidden: true }))
})
it('marks the spawned PTY runtime-session-owned', async () => {
const { runtime } = createRuntimeWithAttachedWindow()
await runtime.createTerminal('id:repo-1::/tmp/wt-cli', {})
const pty = (
runtime as unknown as {
ptysById: Map<string, { runtimeSessionOwned: boolean; connected: boolean }>
}
).ptysById.get('repo-1::/tmp/wt-cli@@a1b2c3d4')
expect(pty?.connected).toBe(true)
expect(pty?.runtimeSessionOwned).toBe(true)
})
})