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:
Neil
2026-10-25 07:36:57 -07:00
committed by GitHub
co-authored by Jinwoo Hong
parent e13c47bea9
commit 6458c08764
5 changed files with 40 additions and 5 deletions
@@ -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)
})
})
+1
View File
@@ -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
})
}