test(terminal): cover settled prompt ownership topologies

This commit is contained in:
Jinwoo-H
2026-08-20 05:18:00 -04:00
parent 2fcbd468db
commit 3fc4cf6da6
4 changed files with 594 additions and 22 deletions
+38 -9
View File
@@ -9411,9 +9411,9 @@
],
"platforms": ["macos", "linux", "windows"],
"providers": ["local", "daemon", "ssh", "remote-runtime"],
"coveredPlatforms": ["macos", "windows"],
"coveredProviders": ["local"],
"coverageNotes": "Source-built macOS and physical Windows Electron journeys cover the public CLI through RPC, runtime identity, real PTY/ConPTY writes, and a delayed fake Codex composer. Focused units cover CLI intent, transient wrapper resolution, fresh foreground identity over stale launch metadata, paired-mobile floor takeover before delayed Enter, framing bytes, stale-agent shell fallback, and exact legacy behavior for every other configured agent. SSH, daemon, and remote-runtime live evidence remain explicit gaps.",
"coveredPlatforms": ["macos", "linux", "windows"],
"coveredProviders": ["local", "ssh"],
"coverageNotes": "Source-built macOS and physical Windows Electron journeys cover the public CLI through RPC, runtime identity, real PTY/ConPTY writes, and a delayed fake Codex composer. A real encrypted mobile subscription proves desktop-to-mobile floor takeover with exact fake-agent stdin, and an ephemeral Linux Docker SSH target proves half-open relay process inspection, transient wrapper retry, exact remote input, SSH execution ownership, and unrelated-PTY survival. Daemon and paired remote-runtime live evidence remain explicit gaps.",
"motivatingLinks": [
"https://linear.app/stably/issue/STA-4328",
"https://linear.app/stably/issue/STA-4486",
@@ -9426,6 +9426,7 @@
"commands": [
"pnpm exec vitest run --config config/vitest.config.ts src/cli/handlers/terminal.test.ts src/main/runtime/rpc/terminal-agent-prompt-send.test.ts src/main/runtime/rpc/terminal-send.test.ts src/main/runtime/orca-runtime.test.ts src/shared/agent-prompt-injection.test.ts",
"pnpm run build:cli && pnpm exec playwright test tests/e2e/terminal-send-agent-prompt-submit.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1",
"pnpm run build:relay && ORCA_E2E_SSH_DOCKER=1 pnpm exec playwright test tests/e2e/ssh-docker-agent-prompt-submit.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1",
"pnpm.cmd exec playwright test tests/e2e/terminal-send-agent-prompt-submit.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1",
"node tests/tools/repro-terminal-send-submit.mjs --cli orca --worktree <registered-worktree> --discard-report"
],
@@ -9436,6 +9437,7 @@
"src/main/runtime/orca-runtime.test.ts",
"src/shared/agent-prompt-injection.test.ts",
"tests/e2e/terminal-send-agent-prompt-submit.spec.ts",
"tests/e2e/ssh-docker-agent-prompt-submit.spec.ts",
"tests/tools/repro-terminal-send-submit.mjs"
],
"assertionRefs": [
@@ -9470,7 +9472,16 @@
"assertions": [
"the public source-built CLI submits after a delayed composer render without rescue",
"the fake agent receives the marker and zero premature Enter on macOS and Windows",
"POSIX preserves one bracketed-paste frame while Windows validates the ConPTY-observable payload"
"POSIX preserves one bracketed-paste frame while Windows validates the ConPTY-observable payload",
"a real paired mobile floor takeover blocks delayed desktop Enter with exact PTY input"
]
},
{
"file": "tests/e2e/ssh-docker-agent-prompt-submit.spec.ts",
"assertions": [
"two half-open SSH relay wrapper inspections precede a bounded retry that identifies Codex",
"the remote fake agent receives one post-render Enter and no premature Enter",
"execution stays on the SSH host and an unrelated remote PTY remains writable"
]
}
],
@@ -9519,19 +9530,37 @@
"result": "passed",
"durationSeconds": 12.9,
"summary": "The 1,227-test candidate gate passed with one existing skip. Its three controlled oracles also failed on origin/main and with only the candidate production hunks disabled, then passed together on the candidate: node-to-Codex wrapper settlement, current foreground over stale launch identity, and paired-mobile floor takeover before delayed Enter."
},
{
"date": "2026-08-20",
"runner": "local",
"platform": "macos",
"command": "pnpm run build:cli && pnpm exec playwright test tests/e2e/terminal-send-agent-prompt-submit.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1",
"result": "passed",
"durationSeconds": 34.0,
"summary": "All four source-built Electron prompt journeys passed. The encrypted paired-mobile case recorded desktop then mobile authority, prompt and mobile bytes at fake-agent stdin, no carriage return, and an accepted=false desktop result after takeover."
},
{
"date": "2026-08-20",
"runner": "local",
"platform": "linux",
"command": "pnpm run build:relay && ORCA_E2E_SSH_DOCKER=1 pnpm exec playwright test tests/e2e/ssh-docker-agent-prompt-submit.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1",
"result": "passed",
"durationSeconds": 31.3,
"summary": "The candidate passed against an ephemeral Debian SSH target and real relay with two controlled half-open process reads, bounded wrapper retry, one post-render remote Enter, SSH execution-host proof, and an unrelated live remote PTY. The byte-identical oracle failed when only the settled-wrapper fix was disabled because the send settled through legacy delivery before the retry barrier."
}
],
"runtimeBudget": {
"p95Seconds": 90,
"scope": "focused units plus one isolated Electron CLI/PTY journey"
"p95Seconds": 180,
"scope": "focused units, isolated Electron CLI/paired-mobile journeys, and one Docker SSH relay journey"
},
"flakeHistory": {
"status": "unknown",
"evidence": "The final oracle passed twice in independent macOS app launches and twice on physical Windows. A shared --repeat-each fixture mode failed before test execution because only one seeded worktree loaded; supported isolated invocations were used for product evidence. CI history is not yet collected."
},
"redGreenEvidence": {
"status": "partial",
"evidence": "The fake-composer oracle was red against the installed pre-fix runtime with prematureEnters=1 and rescueSent=true. It also caught a source-built late-identity race with a complete paste frame but one premature Enter before the render gate began resolving foreground identity. The final candidate is green on macOS and Windows with prematureEnters=0 and rescueSent=false."
"status": "complete",
"evidence": "The same controlled unit oracles are red on origin/main and with each production fix disabled, then green together on the candidate. The paired-mobile Electron oracle is green on the candidate, while its byte-identical controlled floor-takeover oracle is red without the delayed authority recheck. The Docker SSH oracle is green on the candidate and red with only settled-wrapper retry disabled, where legacy delivery settles before the retry barrier. Existing macOS and Windows fake-composer evidence remains green with prematureEnters=0 and rescueSent=false."
},
"performanceBudget": {
"required": true,
@@ -9542,7 +9571,7 @@
"Retain mixed-version evidence that old hosts strip the optional intent and new hosts accept old clients without a protocol bump."
],
"knownGaps": [
"Live SSH/daemon/remote-runtime evidence is not yet attached.",
"Live daemon and paired remote-runtime evidence is not yet attached; SSH relay and paired mobile-to-local-runtime evidence are covered.",
"Only Claude and Codex opt into settlement; every other agent retains the legacy behavior until independently validated.",
"A new CLI talking to an old host safely loses the optional field and therefore keeps the old timing behavior until the host updates.",
"The E2E fake agent models the submission race without requiring external agent authentication; live account-backed agents are covered separately."
@@ -0,0 +1,278 @@
import { execFile } from 'node:child_process'
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { promisify } from 'node:util'
import type { Page } from '@stablyai/playwright-test'
import { expect, test } from './helpers/orca-app'
import { waitForActiveWorktree, waitForSessionReady } from './helpers/store'
import {
cleanupDockerSshRelayTarget,
execDockerSshRelayTargetControlCommand,
shellQuote,
startDockerSshRelayTarget,
type DockerSshRelayTarget,
writeDockerSshRelayTargetFile
} from './helpers/docker-ssh-relay-target'
import { connectDockerSshRelayTarget } from './helpers/docker-ssh-relay-connection'
const RUN_DOCKER_SSH = process.env.ORCA_E2E_SSH_DOCKER === '1'
const REMOTE_AGENT = '/tmp/codex'
const REMOTE_INPUT = '/tmp/orca-codex-prompt-input.bin'
const REMOTE_REPORT = '/tmp/orca-codex-prompt-report.json'
const PS_OBSERVE = '/tmp/orca-prompt-ps-observe'
const PS_FAIL = '/tmp/orca-prompt-ps-fail-once'
const PS_ENTERED = '/tmp/orca-prompt-ps-entered'
const PS_RELEASE = '/tmp/orca-prompt-ps-release'
const PS_RETRY = '/tmp/orca-prompt-ps-retry'
const PS_RETRY_ENTERED = '/tmp/orca-prompt-ps-retry-entered'
const PS_RETRY_RELEASE = '/tmp/orca-prompt-ps-retry-release'
const PS_COUNT = '/tmp/orca-prompt-ps-count'
const execFileAsync = promisify(execFile)
type RuntimeTerminalSummary = {
handle: string
ptyId: string | null
executionHostId?: string
}
async function callRuntime<TResult>(page: Page, method: string, params: unknown): Promise<TResult> {
return page.evaluate(
async ({ method, params }) => {
const response = await window.api.runtime.call({ method, params })
if (!response.ok) {
throw new Error(`${response.error.code}: ${response.error.message}`)
}
return response.result
},
{ method, params }
) as Promise<TResult>
}
async function createRemoteTerminal(
page: Page,
worktreeId: string,
command: string,
title: string
): Promise<RuntimeTerminalSummary> {
const result = await callRuntime<{ terminal: RuntimeTerminalSummary }>(page, 'terminal.create', {
worktree: `id:${worktreeId}`,
command,
title,
presentation: 'background'
})
if (!result.terminal.ptyId) {
throw new Error(`Remote terminal ${result.terminal.handle} has no PTY`)
}
return result.terminal
}
function installControlledPs(target: DockerSshRelayTarget): void {
const script = `#!/usr/bin/env bash
if [ -e ${PS_OBSERVE} ]; then
count=0
[ ! -r ${PS_COUNT} ] || count="$(cat ${PS_COUNT})"
printf '%s' "$((count + 1))" > ${PS_COUNT}
if rm -f ${PS_FAIL} 2>/dev/null && [ ! -e ${PS_ENTERED} ]; then
printf entered > ${PS_ENTERED}
while [ ! -e ${PS_RELEASE} ]; do sleep 0.02; done
exit 1
fi
if rm -f ${PS_RETRY} 2>/dev/null && [ ! -e ${PS_RETRY_ENTERED} ]; then
printf retry > ${PS_RETRY_ENTERED}
while [ ! -e ${PS_RETRY_RELEASE} ]; do sleep 0.02; done
exit 1
fi
fi
exec /usr/bin/ps "$@"
`
writeDockerSshRelayTargetFile(target, '/usr/local/bin/ps', script)
execDockerSshRelayTargetControlCommand(target, 'chmod 755 /usr/local/bin/ps')
}
function prepareRemoteAgent(target: DockerSshRelayTarget): void {
const fixture = readFileSync(
path.join(process.cwd(), 'tests', 'tools', 'repro-terminal-send-submit.mjs'),
'utf8'
)
writeDockerSshRelayTargetFile(target, REMOTE_AGENT, fixture)
installControlledPs(target)
}
function remoteFile(target: DockerSshRelayTarget, filePath: string): string {
try {
return execDockerSshRelayTargetControlCommand(
target,
`test -r ${shellQuote(filePath)} && cat ${shellQuote(filePath)}`
)
} catch {
return ''
}
}
function remoteInput(target: DockerSshRelayTarget): string {
try {
const encoded = execDockerSshRelayTargetControlCommand(
target,
`test -r ${shellQuote(REMOTE_INPUT)} && base64 -w0 ${shellQuote(REMOTE_INPUT)}`
)
return Buffer.from(encoded, 'base64').toString('utf8')
} catch {
return ''
}
}
function armHalfOpenInspection(target: DockerSshRelayTarget): void {
execDockerSshRelayTargetControlCommand(
target,
`rm -f ${REMOTE_INPUT} ${REMOTE_REPORT} ${PS_ENTERED} ${PS_RELEASE} ${PS_RETRY_ENTERED} ${PS_RETRY_RELEASE} ${PS_COUNT}; ` +
`: > ${PS_OBSERVE}; : > ${PS_FAIL}; : > ${PS_RETRY}`
)
}
async function waitForRemoteSignal(
target: DockerSshRelayTarget,
filePath: string
): Promise<string> {
const result = await execFileAsync(
'docker',
[
'exec',
target.containerName,
'bash',
'--noprofile',
'--norc',
'-c',
`while [ ! -r ${shellQuote(filePath)} ]; do sleep 0.02; done; cat ${shellQuote(filePath)}`
],
{ encoding: 'utf8', timeout: 10_000 }
)
return result.stdout.trim()
}
test.describe('Docker SSH settled prompt submission', () => {
test.skip(!RUN_DOCKER_SSH, 'Set ORCA_E2E_SSH_DOCKER=1 to run Docker SSH relay tests.')
test.skip(process.platform === 'win32', 'Docker SSH relay tests use POSIX ssh tooling.')
test('waits through a half-open wrapper inspection before one remote Enter', async ({
orcaPage
}, testInfo) => {
test.setTimeout(180_000)
let target: DockerSshRelayTarget | null = null
const terminalHandles: string[] = []
try {
target = startDockerSshRelayTarget(testInfo)
prepareRemoteAgent(target)
await waitForSessionReady(orcaPage)
await waitForActiveWorktree(orcaPage)
const remote = await connectDockerSshRelayTarget(orcaPage, target)
const marker = `SSH_SETTLED_PROMPT_${Date.now()}`
const survivor = await createRemoteTerminal(
orcaPage,
remote.worktreeId,
'bash',
'unrelated remote shell'
)
terminalHandles.push(survivor.handle)
const terminal = await createRemoteTerminal(
orcaPage,
remote.worktreeId,
`node ${REMOTE_AGENT} --fake-agent --report ${REMOTE_REPORT} --marker ${marker} --input-observation ${REMOTE_INPUT} --timeout-ms 60000`,
'remote prompt fixture'
)
terminalHandles.push(terminal.handle)
expect(terminal.ptyId).not.toBe(survivor.ptyId)
await expect
.poll(async () => {
const read = await callRuntime<{ terminal: { tail: string[] } }>(
orcaPage,
'terminal.read',
{ terminal: terminal.handle }
)
return read.terminal.tail.join('\n')
})
.toContain('OpenAI Codex')
expect(terminal.executionHostId).toBe(`ssh:${encodeURIComponent(remote.targetId)}`)
armHalfOpenInspection(target)
const inspectionEntered = waitForRemoteSignal(target, PS_ENTERED)
const inspection = callRuntime(orcaPage, 'terminal.inspectProcess', {
terminal: terminal.handle
})
await expect(inspectionEntered).resolves.toBe('entered')
const prompt = `${marker} remote wrapper payload`
let sendSettled = false
const send = callRuntime<{ send: { accepted: boolean; bytesWritten: number } }>(
orcaPage,
'terminal.send',
{
terminal: terminal.handle,
text: prompt,
enter: true,
agentPrompt: true,
client: { id: 'docker-ssh-e2e', type: 'desktop' }
}
).finally(() => {
sendSettled = true
})
expect(sendSettled).toBe(false)
expect(remoteInput(target)).toBe('')
execDockerSshRelayTargetControlCommand(target, `: > ${PS_RELEASE}`)
await expect(inspection).resolves.toBeTruthy()
await expect(waitForRemoteSignal(target, PS_RETRY_ENTERED)).resolves.toBe('retry')
expect(remoteInput(target)).toBe('')
expect(sendSettled).toBe(false)
execDockerSshRelayTargetControlCommand(target, `: > ${PS_RETRY_RELEASE}`)
await expect(send).resolves.toMatchObject({ send: { accepted: true } })
await expect.poll(() => remoteFile(target!, REMOTE_REPORT)).not.toBe('')
const observedInput = remoteInput(target)
expect(observedInput).toContain(prompt)
expect(observedInput.match(/\r/g)).toHaveLength(1)
expect(JSON.parse(remoteFile(target, REMOTE_REPORT))).toMatchObject({
contractOk: true,
submitted: true,
prematureEnters: 0,
receivedEnters: 1,
markerReceived: true
})
expect(Number(remoteFile(target, PS_COUNT))).toBeGreaterThanOrEqual(2)
const survivorMarker = `SSH_UNRELATED_SURVIVES_${Date.now()}`
await callRuntime(orcaPage, 'terminal.send', {
terminal: survivor.handle,
text: `printf '${survivorMarker}\\n'`,
enter: true,
client: { id: 'docker-ssh-e2e', type: 'desktop' }
})
await expect
.poll(async () => {
const read = await callRuntime<{ terminal: { tail: string[] } }>(
orcaPage,
'terminal.read',
{ terminal: survivor.handle }
)
return read.terminal.tail.join('\n')
})
.toContain(survivorMarker)
testInfo.annotations.push({
type: 'docker-ssh-settled-prompt',
description: `target=${remote.targetId} worktree=${remote.worktreeId} agentPty=${terminal.ptyId} survivorPty=${survivor.ptyId} psCalls=${remoteFile(target, PS_COUNT)}`
})
} finally {
await Promise.all(
terminalHandles.map((terminal) =>
callRuntime(orcaPage, 'terminal.closeTab', { terminal }).catch(() => undefined)
)
)
if (target) {
execDockerSshRelayTargetControlCommand(
target,
`rm -f ${PS_OBSERVE} ${PS_FAIL} ${PS_RELEASE} ${PS_RETRY} ${PS_RETRY_RELEASE}`
)
}
cleanupDockerSshRelayTarget(target)
}
})
})
@@ -1,37 +1,48 @@
import { execFile } from 'node:child_process'
import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { promisify } from 'node:util'
import type { Page } from '@stablyai/playwright-test'
import { expect, test } from './helpers/orca-app'
import { waitForSessionReady } from './helpers/store'
import { parsePairingCode } from '../../src/shared/pairing'
import {
sendRemoteRuntimeRequest,
subscribeRemoteRuntimeRequest
} from '../../src/shared/remote-runtime-client'
const execFileAsync = promisify(execFile)
const fixtureRoot = mkdtempSync(path.join(os.tmpdir(), 'orca-terminal-send-agent-prompt-'))
const fixtureBin = path.join(fixtureRoot, 'bin')
const fixtureReport = path.join(fixtureRoot, 'report.json')
const fixtureInputObservation = path.join(fixtureRoot, 'input.bin')
const fixtureMarker = `ORCA_TERMINAL_SEND_E2E_${process.pid}`
const fixtureScript = path.join(process.cwd(), 'tests', 'tools', 'repro-terminal-send-submit.mjs')
const fakeCodex = path.join(fixtureBin, process.platform === 'win32' ? 'codex.cmd' : 'codex')
mkdirSync(fixtureBin)
writeFileSync(
fakeCodex,
process.platform === 'win32'
? `@echo off\r\n"${process.execPath}" "${fixtureScript}" --fake-agent --report "%ORCA_FAKE_AGENT_REPORT%" --marker "%ORCA_FAKE_AGENT_MARKER%" --allow-unframed-paste %*\r\n`
: `#!/usr/bin/env sh\nexec "${process.execPath}" "${fixtureScript}" --fake-agent --report "$ORCA_FAKE_AGENT_REPORT" --marker "$ORCA_FAKE_AGENT_MARKER" "$@"\n`,
'utf8'
)
if (process.platform !== 'win32') {
chmodSync(fakeCodex, 0o755)
if (process.platform === 'win32') {
writeFileSync(
fakeCodex,
`@echo off\r\n"${process.execPath}" "${fixtureScript}" --fake-agent --report "%ORCA_FAKE_AGENT_REPORT%" --marker "%ORCA_FAKE_AGENT_MARKER%" --input-observation "%ORCA_FAKE_AGENT_INPUT_OBSERVATION%" --allow-unframed-paste %*\r\n`,
'utf8'
)
} else {
symlinkSync(process.execPath, fakeCodex)
}
const fakeCodexCommand =
process.platform === 'win32'
? `"${fakeCodex}"`
: `${shellQuote(fakeCodex)} ${shellQuote(fixtureScript)} --fake-agent --report ${shellQuote(fixtureReport)} --marker ${shellQuote(fixtureMarker)} --input-observation ${shellQuote(fixtureInputObservation)}`
test.use({
seedTestRepo: false,
orcaAppExtraEnv: {
PATH: `${fixtureBin}${path.delimiter}${process.env.PATH ?? ''}`,
ORCA_FAKE_AGENT_REPORT: fixtureReport,
ORCA_FAKE_AGENT_MARKER: fixtureMarker
ORCA_FAKE_AGENT_MARKER: fixtureMarker,
ORCA_FAKE_AGENT_INPUT_OBSERVATION: fixtureInputObservation
}
})
@@ -59,7 +70,7 @@ test('CLI text plus Enter waits for a slow agent composer before submitting', as
'--worktree',
testRepoPath,
'--agent-command',
'codex',
fakeCodexCommand,
'--report',
fixtureReport,
'--marker',
@@ -109,7 +120,7 @@ test('CLI reports a swallowed Enter without submitting a second Enter', async ({
'--worktree',
testRepoPath,
'--agent-command',
'codex --swallow-first-enter',
`${fakeCodexCommand} --swallow-first-enter`,
'--expect-stalled',
'--report',
fixtureReport,
@@ -189,3 +200,251 @@ test('CLI does not write prompt bytes into an active permission dialog', async (
markerReceived: false
})
})
test('paired mobile floor takeover blocks the CLI delayed Enter', async ({
electronApp,
orcaPage,
testRepoPath
}) => {
test.setTimeout(90_000)
await waitForSessionReady(orcaPage)
const userDataDir = await electronApp.evaluate(({ app }) => app.getPath('userData'))
const marker = fixtureMarker
rmSync(fixtureReport, { force: true })
rmSync(fixtureInputObservation, { force: true })
const worktreeSelector = await resolveTestWorktreeSelector(testRepoPath, userDataDir)
const created = await callDevCli(
[
'terminal',
'create',
'--worktree',
worktreeSelector,
'--title',
'paired mobile prompt authority',
'--command',
`${fakeCodexCommand} --timeout-ms 60000`
],
testRepoPath,
userDataDir
)
const terminal = created.terminal as { handle: string }
const shown = await callDevCli(
['terminal', 'show', '--terminal', terminal.handle],
testRepoPath,
userDataDir
)
const ptyId = (shown.terminal as { ptyId: string }).ptyId
await expect
.poll(async () => {
const ready = await callDevCli(
['terminal', 'show', '--terminal', terminal.handle],
testRepoPath,
userDataDir
)
return (ready.terminal as { preview?: string }).preview
})
.toContain('OpenAI Codex')
const pairingResult = await orcaPage.evaluate(() =>
window.api.mobile.getPairingQR({
address: '127.0.0.1',
connectionMode: 'local-only',
rotate: true
})
)
expect(pairingResult.available).toBe(true)
if (!pairingResult.available) {
throw new Error('Mobile pairing unavailable')
}
const pairing = parsePairingCode(pairingResult.pairingUrl)
expect(pairing).toBeTruthy()
if (!pairing) {
throw new Error('Mobile pairing URL did not parse')
}
const responses: Record<string, unknown>[] = []
const subscription = await subscribeRemoteRuntimeRequest(
pairing,
'terminal.subscribe',
{
terminal: terminal.handle,
client: { id: pairing.deviceToken, type: 'mobile' },
viewport: { cols: 45, rows: 20 },
capabilities: { terminalBinaryStream: 1 }
},
15_000,
{
onResponse: (response) => responses.push(response as unknown as Record<string, unknown>),
onError: (error) => {
throw error
}
}
)
try {
await expect
.poll(() =>
responses.some(
(response) => (response.result as { type?: string } | undefined)?.type === 'subscribed'
)
)
.toBe(true)
await expectTerminalDriver(orcaPage, ptyId, 'mobile')
await expect(
orcaPage.evaluate((id) => window.api.runtime.restoreTerminalFit(id), ptyId)
).resolves.toEqual({ restored: true })
await expectTerminalDriver(orcaPage, ptyId, 'desktop')
const beforeSend = await callDevCli(
['terminal', 'show', '--terminal', terminal.handle],
testRepoPath,
userDataDir
)
expect(beforeSend.terminal).toMatchObject({ connected: true, writable: true, ptyId })
const prompt = `${marker} paired owner payload`
let desktopSendOutcome: unknown
const desktopSend = callDevCli(
['terminal', 'send', '--terminal', terminal.handle, '--text', prompt, '--enter'],
testRepoPath,
userDataDir
)
void desktopSend.then(
(value) => {
desktopSendOutcome = value
},
(error) => {
desktopSendOutcome = error
}
)
await expect
.poll(
() =>
readObservedInput(fixtureInputObservation).includes(marker) ||
desktopSendOutcome !== undefined
)
.toBe(true)
if (!readObservedInput(fixtureInputObservation).includes(marker)) {
const drivers = await orcaPage.evaluate(() => window.api.runtime.getTerminalDrivers())
throw new Error(
`Desktop send settled before paste: ${JSON.stringify(desktopSendOutcome)}; drivers=${JSON.stringify(drivers)}`
)
}
const mobileSend = await sendRemoteRuntimeRequest<{
send: { accepted: boolean; bytesWritten: number }
}>(
pairing,
'terminal.send',
{
terminal: terminal.handle,
text: 'mobile-owner-input',
client: { id: pairing.deviceToken, type: 'mobile' }
},
15_000
)
expect(mobileSend.ok).toBe(true)
if (!mobileSend.ok) {
throw new Error(mobileSend.error.message)
}
expect(mobileSend).toMatchObject({
ok: true,
result: { send: { accepted: true } }
})
await expectTerminalDriver(orcaPage, ptyId, 'mobile')
await expect(desktopSend).resolves.toMatchObject({
send: { accepted: false, bytesWritten: 0 }
})
const observedInput = readObservedInput(fixtureInputObservation)
expect(observedInput).toContain(marker)
expect(observedInput).toContain('mobile-owner-input')
expect(observedInput).not.toContain('\r')
} finally {
subscription.close()
await callDevCli(
['terminal', 'close', '--terminal', terminal.handle],
testRepoPath,
userDataDir
)
}
})
async function resolveTestWorktreeSelector(
testRepoPath: string,
userDataDir: string
): Promise<string> {
const added = await callDevCli(['repo', 'add', '--path', testRepoPath], testRepoPath, userDataDir)
const repoId = (added.repo as { id: string }).id
const deadline = Date.now() + 10_000
while (Date.now() < deadline) {
const listed = await callDevCli(
['worktree', 'list', '--repo', `id:${repoId}`],
testRepoPath,
userDataDir
)
const worktree = (listed.worktrees as { id: string; path: string }[]).find(
(candidate) => path.resolve(candidate.path) === path.resolve(testRepoPath)
)
if (worktree) {
return `id:${worktree.id}`
}
await new Promise((resolve) => setTimeout(resolve, 100))
}
throw new Error(`Test worktree did not materialize for ${repoId}`)
}
async function callDevCli(
args: string[],
cwd: string,
userDataDir: string
): Promise<Record<string, unknown>> {
let result: { stdout: string; stderr: string }
try {
result = await execFileAsync(
process.execPath,
[path.join(process.cwd(), 'config', 'scripts', 'orca-dev.mjs'), ...args, '--json'],
{
cwd,
env: { ...process.env, ORCA_DEV_USER_DATA_PATH: userDataDir },
timeout: 60_000
}
)
} catch (error) {
const failed = error as Error & { stdout?: string; stderr?: string }
throw new Error([failed.message, failed.stdout, failed.stderr].filter(Boolean).join('\n'))
}
const response = JSON.parse(result.stdout) as {
ok: boolean
result?: Record<string, unknown>
error?: { message?: string }
}
if (!response.ok || !response.result) {
throw new Error(response.error?.message ?? 'Orca CLI request failed')
}
return response.result
}
async function expectTerminalDriver(
page: Page,
ptyId: string,
kind: 'desktop' | 'mobile'
): Promise<void> {
await expect
.poll(async () => {
const drivers = await page.evaluate(() => window.api.runtime.getTerminalDrivers())
return drivers.find((entry) => entry.ptyId === ptyId)?.driver.kind
})
.toBe(kind)
}
function shellQuote(value: string): string {
return `'${value.replaceAll("'", "'\\''")}'`
}
function readObservedInput(filePath: string): string {
try {
return readFileSync(filePath, 'utf8')
} catch {
return ''
}
}
@@ -1,5 +1,6 @@
#!/usr/bin/env node
import { spawn } from 'node:child_process'
import { appendFileSync } from 'node:fs'
import { mkdir, readFile, rm, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'
@@ -280,6 +281,7 @@ async function parentMain() {
async function fakeAgentMain() {
const reportPath = argValue('report')
const marker = argValue('marker')
const inputObservationPath = argValue('input-observation')
const timeoutMs = parsePositiveInteger('timeout-ms', DEFAULT_TIMEOUT_MS)
const pasteFramingRequired = !hasFlag('allow-unframed-paste')
const swallowFirstEnter = hasFlag('swallow-first-enter')
@@ -339,6 +341,9 @@ async function fakeAgentMain() {
setTimeout(() => void writeReport(false), 250)
}
process.stdin.on('data', (chunk) => {
if (inputObservationPath) {
appendFileSync(inputObservationPath, chunk)
}
input += chunk.toString('utf8')
if (!renderScheduled && input.includes(marker)) {
renderScheduled = true
@@ -370,6 +375,7 @@ async function fakeAgentMain() {
return
}
prematureEnters += 1
void writeReport(false)
nextCarriage = input.indexOf('\r', countedCarriages)
}
})