mirror of
https://github.com/stablyai/orca.git
synced 2026-10-22 16:03:47 +00:00
Fix WSL orchestration CLI preambles (#8561)
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { resolveTerminalOrchestrationCliCommand } from './cli-command'
|
||||
|
||||
describe('resolveTerminalOrchestrationCliCommand', () => {
|
||||
it('uses orca-ide for a pane recorded as WSL', () => {
|
||||
expect(
|
||||
resolveTerminalOrchestrationCliCommand({
|
||||
connectionId: null,
|
||||
isWsl: true,
|
||||
worktreeId: 'repo::C:\\repo'
|
||||
})
|
||||
).toBe('orca-ide')
|
||||
})
|
||||
|
||||
it('uses project runtime and WSL paths when restored pane metadata is unavailable', () => {
|
||||
expect(
|
||||
resolveTerminalOrchestrationCliCommand({
|
||||
connectionId: null,
|
||||
isWsl: null,
|
||||
worktreeId: 'repo::C:\\repo',
|
||||
projectRuntime: {
|
||||
status: 'resolved',
|
||||
runtime: {
|
||||
kind: 'wsl',
|
||||
hostPlatform: 'wsl',
|
||||
projectId: 'project',
|
||||
distro: 'Ubuntu',
|
||||
reason: 'project-override',
|
||||
cacheKey: 'project:wsl:Ubuntu'
|
||||
}
|
||||
}
|
||||
})
|
||||
).toBe('orca-ide')
|
||||
expect(
|
||||
resolveTerminalOrchestrationCliCommand({
|
||||
connectionId: null,
|
||||
isWsl: null,
|
||||
worktreeId: 'repo::\\\\wsl.localhost\\Ubuntu\\home\\alice\\repo'
|
||||
})
|
||||
).toBe('orca-ide')
|
||||
})
|
||||
|
||||
it('preserves native and SSH bare-orca commands', () => {
|
||||
expect(
|
||||
resolveTerminalOrchestrationCliCommand({
|
||||
connectionId: null,
|
||||
isWsl: false,
|
||||
worktreeId: 'repo::/home/alice/repo'
|
||||
})
|
||||
).toBe('orca')
|
||||
expect(
|
||||
resolveTerminalOrchestrationCliCommand({
|
||||
connectionId: 'ssh-1',
|
||||
isWsl: null,
|
||||
worktreeId: 'repo::\\\\wsl.localhost\\Ubuntu\\home\\alice\\repo'
|
||||
})
|
||||
).toBe('orca')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { ProjectExecutionRuntimeResolution } from '../../../shared/project-execution-runtime'
|
||||
import { isWslUncPath } from '../../../shared/wsl-paths'
|
||||
import { splitWorktreeIdForFilesystem } from '../../../shared/worktree-id'
|
||||
|
||||
export type OrchestrationCliCommand = 'orca' | 'orca-ide'
|
||||
|
||||
export function resolveTerminalOrchestrationCliCommand(args: {
|
||||
connectionId: string | null
|
||||
isWsl: boolean | null | undefined
|
||||
worktreeId: string
|
||||
projectRuntime?: ProjectExecutionRuntimeResolution
|
||||
}): OrchestrationCliCommand {
|
||||
if (args.connectionId) {
|
||||
return 'orca'
|
||||
}
|
||||
if (args.isWsl !== null && args.isWsl !== undefined) {
|
||||
return args.isWsl ? 'orca-ide' : 'orca'
|
||||
}
|
||||
if (args.projectRuntime?.status === 'resolved' && args.projectRuntime.runtime.kind === 'wsl') {
|
||||
return 'orca-ide'
|
||||
}
|
||||
|
||||
const worktreePath = splitWorktreeIdForFilesystem(args.worktreeId)?.worktreePath
|
||||
return worktreePath && isWslUncPath(worktreePath) ? 'orca-ide' : 'orca'
|
||||
}
|
||||
@@ -21,6 +21,7 @@ function createMockRuntime(): CoordinatorRuntime & {
|
||||
createdTerminalOptions: { title?: string }[]
|
||||
probeDriftCalls: string[]
|
||||
probeDriftResult: DriftResult
|
||||
cliCommand: 'orca' | 'orca-ide'
|
||||
setProbeDrift(result: DriftResult): void
|
||||
throwProbeDrift: Error | null
|
||||
} {
|
||||
@@ -36,6 +37,7 @@ function createMockRuntime(): CoordinatorRuntime & {
|
||||
createdTerminalOptions: [] as { title?: string }[],
|
||||
probeDriftCalls: [] as string[],
|
||||
probeDriftResult: null as DriftResult,
|
||||
cliCommand: 'orca' as 'orca' | 'orca-ide',
|
||||
throwProbeDrift: null as Error | null,
|
||||
setProbeDrift(result: DriftResult): void {
|
||||
mock.probeDriftResult = result
|
||||
@@ -63,6 +65,9 @@ function createMockRuntime(): CoordinatorRuntime & {
|
||||
throw mock.throwProbeDrift
|
||||
}
|
||||
return mock.probeDriftResult
|
||||
},
|
||||
getTerminalOrchestrationCliCommand() {
|
||||
return mock.cliCommand
|
||||
}
|
||||
}
|
||||
return mock
|
||||
@@ -121,6 +126,7 @@ describe('Coordinator', () => {
|
||||
it('dispatches a ready task to an available terminal', async () => {
|
||||
db = new OrchestrationDb(':memory:')
|
||||
const runtime = createMockRuntime()
|
||||
runtime.cliCommand = 'orca-ide'
|
||||
runtime.terminals = [{ handle: 'term_a', worktreeId: 'wt1', connected: true, writable: true }]
|
||||
|
||||
const task = db.createTask({ spec: 'implement feature' })
|
||||
@@ -147,6 +153,7 @@ describe('Coordinator', () => {
|
||||
expect(result.status).toBe('completed')
|
||||
expect(result.completedTasks).toContain(task.id)
|
||||
expect(runtime.sentMessages.length).toBeGreaterThan(0)
|
||||
expect(runtime.sentMessages[0].text).toContain('orca-ide orchestration send')
|
||||
})
|
||||
|
||||
it('records the assignee pane key when the runtime can resolve one', async () => {
|
||||
|
||||
@@ -32,6 +32,9 @@ export type CoordinatorRuntime = {
|
||||
// Why: optional so lightweight runtime fakes keep compiling; when present,
|
||||
// dispatch records the remint-stable pane identity of the assignee.
|
||||
getTerminalPaneKey?(handle: string): string | null
|
||||
// Why: Windows can host native and WSL workers concurrently, so the
|
||||
// worker pane—not the coordinator process—selects the packaged CLI name.
|
||||
getTerminalOrchestrationCliCommand?(handle: string): 'orca' | 'orca-ide'
|
||||
}
|
||||
|
||||
// Why (§3.1): single threshold, no warn/refuse split. Coordinator picked 20
|
||||
@@ -486,6 +489,9 @@ export class Coordinator {
|
||||
coordinatorHandle: this.opts.coordinatorHandle,
|
||||
workerHandle: targetHandle,
|
||||
devMode: process.env.ORCA_USER_DATA_PATH?.includes('orca-dev'),
|
||||
...(this.runtime.getTerminalOrchestrationCliCommand
|
||||
? { cliCommand: this.runtime.getTerminalOrchestrationCliCommand(targetHandle) }
|
||||
: {}),
|
||||
// Why (§3.2): drift section fires only when behind > 0. The preamble
|
||||
// builder gates on this itself; passing the object unconditionally lets
|
||||
// the coordinator stay dumb about the display rule.
|
||||
|
||||
@@ -159,7 +159,7 @@ describe('buildDispatchPreamble', () => {
|
||||
})
|
||||
|
||||
it('uses orca-dev CLI when devMode is true', () => {
|
||||
const result = buildDispatchPreamble(baseParams({ devMode: true }))
|
||||
const result = buildDispatchPreamble(baseParams({ devMode: true, cliCommand: 'orca-ide' }))
|
||||
expect(result).toContain('orca-dev orchestration send')
|
||||
expect(result).toContain('orca-dev orchestration check')
|
||||
expect(result).toContain('orca-dev orchestration ask')
|
||||
@@ -175,6 +175,15 @@ describe('buildDispatchPreamble', () => {
|
||||
expect(result).toContain('orca orchestration check')
|
||||
})
|
||||
|
||||
it('uses the exact orca-ide command for packaged WSL workers', () => {
|
||||
const result = buildDispatchPreamble(baseParams({ cliCommand: 'orca-ide' }))
|
||||
|
||||
expect(result).toContain('orca-ide orchestration send')
|
||||
expect(result).toContain('orca-ide orchestration check')
|
||||
expect(result).toContain('orca-ide orchestration ask')
|
||||
expect(result).not.toMatch(/(^|\s)orca orchestration/m)
|
||||
})
|
||||
|
||||
it('appends a BASE DRIFT section when baseDrift.behind > 0', () => {
|
||||
const result = buildDispatchPreamble({
|
||||
taskId: 'task_x',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { OrchestrationCliCommand } from './cli-command'
|
||||
|
||||
export type PreambleParams = {
|
||||
taskId: string
|
||||
// Why: completion and heartbeat payloads attribute activity to a specific
|
||||
@@ -10,6 +12,9 @@ export type PreambleParams = {
|
||||
coordinatorHandle: string
|
||||
workerHandle: string
|
||||
devMode?: boolean
|
||||
// Why: packaged WSL panes install the scoped launcher as `orca-ide`;
|
||||
// other execution hosts keep their existing bare `orca` bridge.
|
||||
cliCommand?: OrchestrationCliCommand
|
||||
// Why: populated by the coordinator's dispatch pre-flight (§3.1) only
|
||||
// when the target worktree is behind its tracking remote. When absent
|
||||
// or when `behind === 0`, the preamble emits no drift section. Callers
|
||||
@@ -42,7 +47,7 @@ export function buildDispatchPreamble(params: PreambleParams): string {
|
||||
// Why: in dev mode, agents must use orca-dev to connect to the dev runtime's
|
||||
// socket. Without this, agents inside the dev Electron app would call the
|
||||
// production CLI and talk to the wrong Orca instance (Section 6.4).
|
||||
const cli = params.devMode ? 'orca-dev' : 'orca'
|
||||
const cli = params.devMode ? 'orca-dev' : (params.cliCommand ?? 'orca')
|
||||
const postDoneInstructions = buildPostWorkerDoneInstructions({
|
||||
cli,
|
||||
workerKind: params.workerKind ?? 'prompt-returning-agent'
|
||||
|
||||
Reference in New Issue
Block a user