Fix WSL orchestration CLI preambles (#8561)
This commit is contained in:
parent
6eda9cdb27
commit
17f032dbd5
|
|
@ -1702,6 +1702,7 @@ describe('registerPtyHandlers', () => {
|
|||
// (the GNOME screen reader) inside Orca-managed terminals (#7904).
|
||||
expect(entries.indexOf(shimDir)).toBeGreaterThanOrEqual(0)
|
||||
expect(entries.indexOf(shimDir)).toBeLessThan(entries.indexOf('/usr/bin'))
|
||||
expect(env.ORCA_CLI_COMMAND).toBeUndefined()
|
||||
} finally {
|
||||
Object.defineProperty(process, 'platform', {
|
||||
configurable: true,
|
||||
|
|
@ -1801,10 +1802,13 @@ describe('registerPtyHandlers', () => {
|
|||
// Why: runtime-created spawns (e.g. the mobile-create materialize path)
|
||||
// must thread the same {tabId, leafId} so the catch-path rescue can find
|
||||
// and keep their live PTY (#7587).
|
||||
expect(runtime.registerPty).toHaveBeenCalledWith(expect.any(String), 'wt-runtime', null, {
|
||||
tabId: 'tab-1',
|
||||
leafId
|
||||
})
|
||||
expect(runtime.registerPty).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'wt-runtime',
|
||||
null,
|
||||
{ tabId: 'tab-1', leafId },
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('uses the owning project WSL runtime for runtime-created daemon PTYs', async () => {
|
||||
|
|
@ -1859,6 +1863,13 @@ describe('registerPtyHandlers', () => {
|
|||
expect(spawnOptions.shellOverride).toBe('wsl.exe')
|
||||
expect(spawnOptions.terminalWindowsWslDistro).toBe('Ubuntu')
|
||||
expect(spawnOptions.terminalWindowsPowerShellImplementation).toBe('auto')
|
||||
expect(runtime.registerPty).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'repo-1::C:\\repo',
|
||||
null,
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -4558,10 +4569,13 @@ describe('registerPtyHandlers', () => {
|
|||
|
||||
// Why: this is the load-bearing wiring for #7587 — the runtime can only back a
|
||||
// stalled mobile create from a live spawn if the spawn threads {tabId, leafId}.
|
||||
expect(runtime.registerPty).toHaveBeenCalledWith(expect.any(String), 'wt-1', null, {
|
||||
tabId: 'tab-1',
|
||||
leafId
|
||||
})
|
||||
expect(runtime.registerPty).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'wt-1',
|
||||
null,
|
||||
{ tabId: 'tab-1', leafId },
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('omits the pane identity from registerPty when the leafId is not a terminal leaf (#7587)', async () => {
|
||||
|
|
@ -4590,7 +4604,13 @@ describe('registerPtyHandlers', () => {
|
|||
// Why: legacy numeric pane ids (`pane:N`) are not terminal leaf ids, so the
|
||||
// spawn seam must not fabricate a binding for them (registerPty would ignore
|
||||
// it anyway); this pins that the seam passes a clean `undefined`.
|
||||
expect(runtime.registerPty).toHaveBeenCalledWith(expect.any(String), 'wt-1', null, undefined)
|
||||
expect(runtime.registerPty).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'wt-1',
|
||||
null,
|
||||
undefined,
|
||||
false
|
||||
)
|
||||
})
|
||||
|
||||
it('refreshes native Agent Teams env when captured teammate mode lives in launch args', async () => {
|
||||
|
|
@ -6069,10 +6089,12 @@ describe('registerPtyHandlers', () => {
|
|||
expect(spawnCall[0]).toBe('wsl.exe')
|
||||
expect(env.ORCA_TERMINAL_HANDLE).toBe('term_wsl')
|
||||
expect(env.ORCA_USER_DATA_PATH).toBe('/tmp/orca-user-data')
|
||||
expect(env.ORCA_CLI_COMMAND).toBe('orca-ide')
|
||||
expect(env.WSLENV?.split(':')).toEqual(
|
||||
expect.arrayContaining([
|
||||
'ORCA_TERMINAL_HANDLE/u',
|
||||
'ORCA_USER_DATA_PATH/p',
|
||||
'ORCA_CLI_COMMAND/u',
|
||||
'ORCA_AGENT_HOOK_PORT/u',
|
||||
'ORCA_AGENT_HOOK_TOKEN/u',
|
||||
'ORCA_OMP_SOURCE_AGENT_DIR/p',
|
||||
|
|
|
|||
|
|
@ -980,8 +980,14 @@ export function buildPtyHostEnv(
|
|||
// Why: WSL shells need the managed userData root for shell-ready wrappers; dev-mode terminals need the same export so `orca` targets the live dev instance.
|
||||
if (opts.isWsl) {
|
||||
baseEnv.ORCA_USER_DATA_PATH = opts.userDataPath
|
||||
} else if (!opts.isPackaged) {
|
||||
baseEnv.ORCA_USER_DATA_PATH ??= opts.userDataPath
|
||||
// Why: managed WSL registration deliberately uses `orca-ide`; exposing
|
||||
// that literal keeps agent guidance scoped to WSL without a bare-orca shim.
|
||||
baseEnv.ORCA_CLI_COMMAND = opts.isPackaged ? 'orca-ide' : 'orca-dev'
|
||||
} else {
|
||||
if (!opts.isPackaged) {
|
||||
baseEnv.ORCA_USER_DATA_PATH ??= opts.userDataPath
|
||||
}
|
||||
delete baseEnv.ORCA_CLI_COMMAND
|
||||
}
|
||||
// Why: dev mode needs the launcher PATH override so `orca` resolves to the dev build instead of the production binary at /usr/local/bin/orca.
|
||||
if (!opts.isPackaged) {
|
||||
|
|
@ -3234,6 +3240,9 @@ export function registerPtyHandlers(
|
|||
args.tabId.length <= 512 &&
|
||||
metadataLeafId !== null
|
||||
? { tabId: args.tabId, leafId: metadataLeafId }
|
||||
: undefined,
|
||||
!args.connectionId
|
||||
? shouldSkipCodexHomeEnvForWindowsShell(daemonShellOverride, cwd)
|
||||
: undefined
|
||||
)
|
||||
}
|
||||
|
|
@ -4282,6 +4291,9 @@ export function registerPtyHandlers(
|
|||
args.tabId.length <= 512 &&
|
||||
metadataLeafId !== null
|
||||
? { tabId: args.tabId, leafId: metadataLeafId }
|
||||
: undefined,
|
||||
!args.connectionId
|
||||
? shouldSkipCodexHomeEnvForWindowsShell(effectiveShellOverride, cwd)
|
||||
: undefined
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ describe('addOrcaWslInteropEnv', () => {
|
|||
const env: Record<string, string> = {
|
||||
ORCA_TERMINAL_HANDLE: 'term_wsl',
|
||||
ORCA_USER_DATA_PATH: 'C:\\Users\\jin\\AppData\\Roaming\\Orca',
|
||||
ORCA_CLI_COMMAND: 'orca-ide',
|
||||
ORCA_OMP_STATUS_EXTENSION: 'C:\\Users\\jin\\.omp\\agent\\extensions\\orca-agent-status.ts',
|
||||
ORCA_PANE_KEY: 'tab-1:leaf-1',
|
||||
ORCA_TAB_ID: 'tab-1',
|
||||
|
|
@ -38,6 +39,7 @@ describe('addOrcaWslInteropEnv', () => {
|
|||
|
||||
expect(env.WSLENV).toContain('ORCA_TERMINAL_HANDLE/u')
|
||||
expect(env.WSLENV).toContain('ORCA_USER_DATA_PATH/p')
|
||||
expect(env.WSLENV).toContain('ORCA_CLI_COMMAND/u')
|
||||
expect(env.WSLENV).toContain('ORCA_OMP_STATUS_EXTENSION/p')
|
||||
expect(env.WSLENV).toContain('ORCA_PANE_KEY/u')
|
||||
expect(env.WSLENV).toContain('ORCA_TAB_ID/u')
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export function addOrcaWslInteropEnv(env: Record<string, string>): void {
|
|||
const passthroughEntries = [
|
||||
'ORCA_TERMINAL_HANDLE/u',
|
||||
'ORCA_USER_DATA_PATH/p',
|
||||
'ORCA_CLI_COMMAND/u',
|
||||
'ORCA_PANE_KEY/u',
|
||||
'ORCA_TAB_ID/u',
|
||||
'ORCA_WORKTREE_ID/u',
|
||||
|
|
|
|||
|
|
@ -469,7 +469,9 @@ import {
|
|||
resolveLocalProjectRuntimeForRepo,
|
||||
resolveLocalProjectRuntimesForRepos
|
||||
} from '../project-runtime-git-options'
|
||||
import { resolveLocalProjectRuntimeForWorktreeId } from '../local-project-runtime-resolution'
|
||||
import type { ProjectExecutionRuntimeResolution } from '../../shared/project-execution-runtime'
|
||||
import { resolveTerminalOrchestrationCliCommand } from './orchestration/cli-command'
|
||||
import {
|
||||
getLocalWorktreePathAccess,
|
||||
removeLocalWorktreePath,
|
||||
|
|
@ -976,6 +978,9 @@ type RuntimePtyWorktreeRecord = {
|
|||
ptyId: string
|
||||
worktreeId: string
|
||||
connectionId: string | null
|
||||
// Why: a Windows host can own both native and WSL panes; preamble command
|
||||
// selection must follow the pane that executes it, not process.platform.
|
||||
isWsl: boolean | null
|
||||
// Why: background CLI PTYs can outlive a failed renderer reveal. Preserve the
|
||||
// spawn-time tab/pane identity so later reveals can adopt under the env key.
|
||||
tabId: string | null
|
||||
|
|
@ -5529,7 +5534,8 @@ export class OrcaRuntimeService {
|
|||
ptyId: string,
|
||||
worktreeId: string,
|
||||
connectionId: string | null = null,
|
||||
binding?: { tabId: string; leafId: string }
|
||||
binding?: { tabId: string; leafId: string },
|
||||
isWsl?: boolean
|
||||
): void {
|
||||
// Why: record the renderer pane identity at spawn time so a stalled graph
|
||||
// sync can't hide that a live PTY already backs a pending mobile create.
|
||||
|
|
@ -5540,6 +5546,7 @@ export class OrcaRuntimeService {
|
|||
this.recordPtyWorktree(ptyId, worktreeId, {
|
||||
connected: true,
|
||||
connectionId,
|
||||
...(isWsl !== undefined ? { isWsl } : {}),
|
||||
...(binding && paneKey ? { tabId: binding.tabId, paneKey } : {})
|
||||
})
|
||||
// Why: the renderer's own PTY spawn is the reliable signal that the pending
|
||||
|
|
@ -7234,6 +7241,27 @@ export class OrcaRuntimeService {
|
|||
return pty ? { worktreeId: pty.worktreeId, connectionId: pty.connectionId } : null
|
||||
}
|
||||
|
||||
getTerminalOrchestrationCliCommand(handle: string): 'orca' | 'orca-ide' {
|
||||
let pty: RuntimePtyWorktreeRecord | null = null
|
||||
try {
|
||||
const ptyId = this.resolveLeafForHandle(handle)?.ptyId
|
||||
pty = ptyId ? (this.ptysById.get(ptyId) ?? null) : null
|
||||
} catch {
|
||||
return 'orca'
|
||||
}
|
||||
if (!pty) {
|
||||
return 'orca'
|
||||
}
|
||||
return resolveTerminalOrchestrationCliCommand({
|
||||
connectionId: pty.connectionId,
|
||||
isWsl: pty.isWsl,
|
||||
worktreeId: pty.worktreeId,
|
||||
projectRuntime: this.store
|
||||
? resolveLocalProjectRuntimeForWorktreeId(this.requireStore(), pty.worktreeId)
|
||||
: undefined
|
||||
})
|
||||
}
|
||||
|
||||
hasRecentTerminalOutputPath(handle: string, pathText: string, absolutePath: string): boolean {
|
||||
const ptyId = this.resolveLeafForHandle(handle)?.ptyId
|
||||
const recentOutput = ptyId ? this.recentPtyOutputById.get(ptyId) : null
|
||||
|
|
@ -20057,7 +20085,14 @@ export class OrcaRuntimeService {
|
|||
state: Partial<
|
||||
Pick<
|
||||
RuntimePtyWorktreeRecord,
|
||||
'connected' | 'lastOutputAt' | 'preview' | 'tabId' | 'paneKey' | 'title' | 'connectionId'
|
||||
| 'connected'
|
||||
| 'lastOutputAt'
|
||||
| 'preview'
|
||||
| 'tabId'
|
||||
| 'paneKey'
|
||||
| 'title'
|
||||
| 'connectionId'
|
||||
| 'isWsl'
|
||||
>
|
||||
> = {}
|
||||
): RuntimePtyWorktreeRecord {
|
||||
|
|
@ -20068,6 +20103,7 @@ export class OrcaRuntimeService {
|
|||
ptyId,
|
||||
worktreeId,
|
||||
connectionId: state.connectionId ?? parseAppSshPtyId(ptyId)?.connectionId ?? null,
|
||||
isWsl: state.isWsl ?? null,
|
||||
tabId: state.tabId ?? null,
|
||||
paneKey: state.paneKey ?? null,
|
||||
launchConfig: null,
|
||||
|
|
@ -20109,6 +20145,9 @@ export class OrcaRuntimeService {
|
|||
if (state.connectionId !== undefined) {
|
||||
pty.connectionId = state.connectionId
|
||||
}
|
||||
if (state.isWsl !== undefined) {
|
||||
pty.isWsl = state.isWsl
|
||||
}
|
||||
if (state.tabId !== undefined) {
|
||||
pty.tabId = state.tabId
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -1369,6 +1369,22 @@ describe('orchestration RPC methods', () => {
|
|||
)
|
||||
})
|
||||
|
||||
it('uses the target pane CLI command for the returned preamble', async () => {
|
||||
setup()
|
||||
const task = db.createTask({ spec: 'work' })
|
||||
vi.spyOn(runtime, 'getTerminalOrchestrationCliCommand').mockReturnValue('orca-ide')
|
||||
|
||||
const result = (await call('orchestration.dispatch', {
|
||||
task: task.id,
|
||||
to: 'term_wsl',
|
||||
returnPreamble: true
|
||||
})) as { preamble: string }
|
||||
|
||||
expect(runtime.getTerminalOrchestrationCliCommand).toHaveBeenCalledWith('term_wsl')
|
||||
expect(result.preamble).toContain('orca-ide orchestration send')
|
||||
expect(result.preamble).not.toMatch(/(^|\s)orca orchestration/m)
|
||||
})
|
||||
|
||||
it('injects preamble through the agent prompt path instead of raw terminal send', async () => {
|
||||
setup()
|
||||
const task = db.createTask({ spec: 'line one\nline two' })
|
||||
|
|
|
|||
|
|
@ -495,7 +495,10 @@ export const ORCHESTRATION_METHODS: RpcMethod[] = [
|
|||
taskSpec: task.spec,
|
||||
coordinatorHandle: params.from ?? 'coordinator',
|
||||
workerHandle: params.to ?? 'worker',
|
||||
devMode: params.devMode
|
||||
devMode: params.devMode,
|
||||
...(params.to
|
||||
? { cliCommand: runtime.getTerminalOrchestrationCliCommand(params.to) }
|
||||
: {})
|
||||
})
|
||||
return { dispatch: null, injected: false, dryRun: true, preamble }
|
||||
}
|
||||
|
|
@ -540,7 +543,8 @@ export const ORCHESTRATION_METHODS: RpcMethod[] = [
|
|||
taskSpec: task.spec,
|
||||
coordinatorHandle: params.from ?? 'coordinator',
|
||||
workerHandle: to,
|
||||
devMode: params.devMode
|
||||
devMode: params.devMode,
|
||||
cliCommand: runtime.getTerminalOrchestrationCliCommand(to)
|
||||
})
|
||||
|
||||
let injected = false
|
||||
|
|
@ -583,6 +587,7 @@ export const ORCHESTRATION_METHODS: RpcMethod[] = [
|
|||
if (!task) {
|
||||
throw new Error(`Task not found: ${params.task}`)
|
||||
}
|
||||
const workerHandle = ctx?.assignee_handle ?? 'worker'
|
||||
const preamble = buildDispatchPreamble({
|
||||
taskId: task.id,
|
||||
// Why: prefer the existing dispatch context's id if we have one
|
||||
|
|
@ -591,8 +596,9 @@ export const ORCHESTRATION_METHODS: RpcMethod[] = [
|
|||
dispatchId: ctx?.id ?? 'ctx_preview',
|
||||
taskSpec: task.spec,
|
||||
coordinatorHandle: params.from ?? 'coordinator',
|
||||
workerHandle: ctx?.assignee_handle ?? 'worker',
|
||||
devMode: params.devMode
|
||||
workerHandle,
|
||||
devMode: params.devMode,
|
||||
...(ctx ? { cliCommand: runtime.getTerminalOrchestrationCliCommand(workerHandle) } : {})
|
||||
})
|
||||
return { dispatch: ctx ?? null, preamble }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue