Files
orca/src/main/runtime/qoder-terminal-command-resolution.test.ts
T
Neil ebeea319b2 Support modern Qoder commands and verify authenticated resume
Start and resume Qoder through the existing execution-host selector when only the documented qoder command exists. Preserve legacy qodercli preference, explicit commands, quoting and session identity; disconnected SSH execution refuses without local fallback.

Mobile history uses command-at-create only with the optional owned-create capability and an existing stable mutation identity. Reconcile authoritative execution-host inventory before retrying creation, adopt the same surviving operation, preserve WSL/incarnation metadata and restore only missing original launch metadata. Changed retry settings cannot replace original capture. Bounded evidence expires after 15 minutes; unavailable inventory or original evidence refuses recovery and leaves surviving execution untouched. Authoritative inventory proving absence preserves the existing recreation behavior; this is not a durable exactly-once ledger for completed one-shot side effects. Older hosts retain the acknowledged create-then-send path.

Scope mobile launch authority to the current committed host/client generation, and recheck operation ownership after asynchronous preparation before later sends. Retire the mutation once the host acknowledges the resume; later ownership changes stop navigation without reporting a completed resume as failed or reusing a cached legacy pane. Preserve genuinely interrupted mutation identity. Preserve current-main OpenCode validation and merged Pi/Cursor behavior. Correct unchanged-main editor test fixtures to their production insertion-range and store contracts while retaining original assertions and production behavior.

Credit: Neil Parker (@nwparker); Soperf and jyang for Qoder integration/history groundwork in #24614; actual Pullfrog and CodeRabbit reviews and the independent Source reviewer for the mobile retry, launch capture, evidence lifetime and connection ownership findings. Conservative positional process recognition and existing folder-history matching-worktree limitations remain documented.
2026-10-04 19:50:45 -07:00

102 lines
3.8 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest'
import { buildAgentStartupPlan, buildAgentResumeStartupPlan } from '../../shared/tui-agent-startup'
import { buildAiVaultResumeShellCommand } from '../../shared/ai-vault-resume-command'
import { resolveQoderTerminalCommand } from './qoder-terminal-command-resolution'
vi.mock('../preflight/agent-detection', () => ({ detectAgentCommandsOnHost: vi.fn() }))
describe('execution-host Qoder command selection', () => {
it.each(['darwin', 'linux', 'win32'] as const)(
'starts and resumes modern-only and legacy-only installs on %s',
async (platform) => {
const startup = buildAgentStartupPlan({
agent: 'qoder',
prompt: 'qodercli is a prompt word',
cmdOverrides: {},
platform
})
const resume = buildAgentResumeStartupPlan({
agent: 'qoder',
providerSession: { key: 'session_id', id: 'existing-session' },
cmdOverrides: {},
platform
})
if (!startup || !resume) {
throw new Error('Missing Qoder plans')
}
const shell = platform === 'win32' ? 'powershell' : 'posix'
for (const found of [
new Set(['qoder']),
new Set(['qodercli']),
new Set(['qoder', 'qodercli'])
]) {
const selected = found.has('qodercli') ? 'qodercli' : 'qoder'
const detect = vi.fn(async () => found)
for (const plan of [startup, resume]) {
const result = await resolveQoderTerminalCommand(
{ launchAgent: 'qoder', command: plan.launchCommand, launchConfig: plan.launchConfig },
{ shell },
detect
)
expect(result.command).toBe(plan.launchCommand.replace(/^qodercli/, selected))
expect(result.launchConfig?.agentCommand).toBe(selected)
}
const historyCommand = buildAiVaultResumeShellCommand({
resumeCommand: resume.launchCommand,
cwd: '/folder workspace',
platform,
shell
})
const history = await resolveQoderTerminalCommand(
{ launchAgent: 'qoder', command: historyCommand, launchConfig: resume.launchConfig },
{ shell },
detect
)
expect(history.command).toBe(historyCommand.replace(/qodercli(?=\s)/, selected))
}
}
)
it('queries each execution host without sharing a result or falling back after contact loss', async () => {
const options = { launchAgent: 'qoder' as const, command: 'qodercli --resume existing-session' }
const detect = vi
.fn()
.mockResolvedValueOnce(new Set(['qoder']))
.mockResolvedValueOnce(new Set(['qodercli']))
.mockRejectedValueOnce(new Error('Host disconnected'))
const wsl = { shell: 'posix' as const, context: { wslDistro: 'Ubuntu' } }
const ssh = { shell: 'posix' as const, connectionId: 'ssh-host' }
expect((await resolveQoderTerminalCommand(options, wsl, detect)).command).toBe(
'qoder --resume existing-session'
)
expect((await resolveQoderTerminalCommand(options, ssh, detect)).command).toBe(options.command)
await expect(resolveQoderTerminalCommand(options, ssh, detect)).rejects.toThrow(
'Host disconnected'
)
expect(detect.mock.calls).toEqual([
[['qodercli', 'qoder'], wsl],
[['qodercli', 'qoder'], ssh],
[['qodercli', 'qoder'], ssh]
])
})
it.each([
'/custom/qodercli --resume id',
'qoder --prompt-interactive hello',
'echo qodercli',
'qodercli-other'
])('preserves explicit paths, modern commands and unrelated commands: %s', async (command) => {
const detect = vi.fn()
expect(
(
await resolveQoderTerminalCommand(
{ launchAgent: 'qoder', command },
{ shell: 'posix' },
detect
)
).command
).toBe(command)
expect(detect).not.toHaveBeenCalled()
})
})