mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(orchestration): re-read an unrecognized foreground before refusing worker-start --terminal (#33544)
`worker-start --terminal` can reuse an idle native-install Claude worker on macOS again: when the cached foreground name isn't a known agent, shell or wrapper (e.g. Claude's `2.1.310` version-named binary), the presence check does one awaited fresh process read (the same read message sending uses, #18238) before refusing. Fixes #32090 Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
This commit is contained in:
@@ -59,6 +59,8 @@ export abstract class OrcaRuntimeWithStopRequestedPtyIds extends OrcaRuntimeWith
|
||||
getTrackedPty: (ptyId) => this.ptysById.get(ptyId) ?? null,
|
||||
getTabTitle: (tabId) => this.tabs.get(tabId)?.title?.trim() || null,
|
||||
getForegroundProcess: (ptyId) => this.ptyController?.getForegroundProcess(ptyId) ?? null,
|
||||
confirmForegroundProcess: (ptyId) =>
|
||||
this.ptyController?.confirmForegroundProcess?.(ptyId) ?? null,
|
||||
getTitleDisplayClear: (ptyId) => this.getPtyTitleDisplayClear(ptyId)
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { committingSpawn } from '../bind-first-pane.test-fixture'
|
||||
import { OrcaRuntimeService } from '../orca-runtime-test-mocks.spec'
|
||||
import { TEST_WORKTREE_PATH, store } from '../orca-runtime-test-fixtures.spec'
|
||||
|
||||
describe('terminal agent presence', () => {
|
||||
it('finds an idle Claude by a fresh read when p_comm is its version directory', async () => {
|
||||
const spawn = vi.fn().mockResolvedValue({ id: 'pty-claude' })
|
||||
const runtime = new OrcaRuntimeService(store)
|
||||
runtime.setPtyController({
|
||||
spawn: committingSpawn(runtime, spawn),
|
||||
write: () => true,
|
||||
kill: () => true,
|
||||
getForegroundProcess: async () => '2.1.310',
|
||||
confirmForegroundProcess: async () => 'claude'
|
||||
})
|
||||
const { handle } = await runtime.createTerminal(`path:${TEST_WORKTREE_PATH}`, {
|
||||
command: 'claude',
|
||||
title: 'worker'
|
||||
})
|
||||
// Claude answered over OSC 7501 paints no title glyph.
|
||||
runtime.onPtyData('pty-claude', '\x1b]0;Orca worker dispatch task ok\x07', 1)
|
||||
|
||||
await expect(runtime.isTerminalRunningAgent(handle)).resolves.toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -69,6 +69,7 @@ await import('./orca-runtime-tests/terminal-output-and-worker-recovery-part-07.s
|
||||
await import('./orca-runtime-tests/terminal-handles-and-agent-status.spec')
|
||||
await import('./orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec')
|
||||
await import('./orca-runtime-tests/terminal-settled-prompt-foreground-confirmation.spec')
|
||||
await import('./orca-runtime-tests/terminal-agent-presence.spec')
|
||||
await import('./orca-runtime-tests/terminal-handles-and-agent-status-part-03.spec')
|
||||
await import('./orca-runtime-tests/terminal-handles-and-agent-status-part-04.spec')
|
||||
await import('./orca-runtime-tests/terminal-handles-and-agent-status-part-05.spec')
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
recognizeAgentProcess
|
||||
} from '../../shared/agent-process-recognition'
|
||||
import { isOpenCodeNativeTitle } from '../../shared/agent-detection'
|
||||
import { isShellProcess } from '../../shared/shell-process-detection'
|
||||
import { isKnownReadyPromptPreview } from './terminal-wait-detection'
|
||||
import { buildTerminalWaitText } from './terminal-wait-tail-state'
|
||||
import type { RuntimeLeafRecord, RuntimePtyWorktreeRecord } from './runtime-terminal-state-records'
|
||||
@@ -31,6 +32,7 @@ type RuntimeTerminalAgentPresenceDependencies = {
|
||||
getTrackedPty(ptyId: string): RuntimePtyWorktreeRecord | null
|
||||
getTabTitle(tabId: string): string | null
|
||||
getForegroundProcess(ptyId: string): Promise<string | null> | null
|
||||
confirmForegroundProcess(ptyId: string): Promise<string | null> | null
|
||||
/** The stale-working timer's display-only clear of the PTY's own title, if one stands. */
|
||||
getTitleDisplayClear(ptyId: string): TitleDisplayClear | null
|
||||
}
|
||||
@@ -197,14 +199,17 @@ export class RuntimeTerminalAgentPresence {
|
||||
suppressClaude: boolean,
|
||||
retryForegroundWrappers: boolean
|
||||
): Promise<boolean> {
|
||||
const recognized = recognizeAgentProcess(foregroundProcess)
|
||||
const wrapper = isAgentForegroundWrapperProcess(foregroundProcess)
|
||||
// Why: macOS p_comm names the native Claude install by its version directory (`2.1.310`).
|
||||
const recognized =
|
||||
recognizeAgentProcess(foregroundProcess) ??
|
||||
(retryForegroundWrappers && !wrapper && !isShellProcess(foregroundProcess)
|
||||
? recognizeAgentProcess(await this.deps.confirmForegroundProcess(ptyId))
|
||||
: null)
|
||||
if (recognized) {
|
||||
return !(suppressClaude && isExpectedAgentProcess(recognized.processName, 'claude'))
|
||||
}
|
||||
if (!isAgentForegroundWrapperProcess(foregroundProcess)) {
|
||||
return false
|
||||
}
|
||||
if (!retryForegroundWrappers) {
|
||||
if (!wrapper || !retryForegroundWrappers) {
|
||||
return false
|
||||
}
|
||||
const startedAt = Date.now()
|
||||
|
||||
@@ -19,6 +19,7 @@ function presence(isLiveStructuredAgent: (handle: string) => boolean) {
|
||||
getTrackedPty: () => null,
|
||||
getTabTitle: () => null,
|
||||
getForegroundProcess: () => null,
|
||||
confirmForegroundProcess: () => null,
|
||||
getTitleDisplayClear: () => null
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user