fix(orchestration): start Antigravity workers in a terminal when its chat runtime isn't installed (#33520)

When Antigravity's Orca-managed chat runtime isn't installed, default-chat launches (orchestration workers, agent.launch from CLI/mobile/automations) fall back to a terminal running agy with reason chat_runtime_not_installed instead of failing at terminal_create; an explicit --surface chat is refused with an install hint. Desktop chat tabs still show the install card.

Fixes #30973
This commit is contained in:
Neil
2026-10-25 04:55:52 -07:00
committed by GitHub
parent 51dcef8258
commit 526ffe38ff
9 changed files with 82 additions and 14 deletions
+15 -6
View File
@@ -68,7 +68,9 @@ const DOWNGRADE_DETAIL: Record<AgentLaunchDowngradeReason, string> = {
structured_support_unknown: 'the execution host has not established structured session support',
wsl_execution_runtime: 'this workspace runs under WSL',
codex_on_windows: 'Codex has no structured session on Windows',
structured_unsupported_on_host: 'the execution host cannot create one here'
structured_unsupported_on_host: 'the execution host cannot create one here',
chat_runtime_not_installed:
"its chat runtime isn't installed on this host (install it from a new chat tab in Orca)"
}
const BLOCKER_REASON: Record<StructuredNativeChatBlocker, AgentLaunchDowngradeReason> = {
@@ -82,10 +84,14 @@ const BLOCKER_REASON: Record<StructuredNativeChatBlocker, AgentLaunchDowngradeRe
'client-capability': 'structured_sessions_unavailable'
}
const HOST_SUPPORT_REASON: Record<'agent' | 'remote' | 'wsl', AgentLaunchDowngradeReason> = {
type HostSupportReason = 'agent' | 'remote' | 'wsl' | 'chat-program'
type HostLaunchSupport = { supported: boolean; reason?: HostSupportReason }
const HOST_SUPPORT_REASON: Record<HostSupportReason, AgentLaunchDowngradeReason> = {
agent: 'structured_unsupported_on_host',
remote: 'remote_execution_host',
wsl: 'wsl_execution_runtime'
wsl: 'wsl_execution_runtime',
'chat-program': 'chat_runtime_not_installed'
}
/** Apply shared feasibility policy to facts available before resolving the workspace. */
@@ -195,12 +201,15 @@ async function readStructuredCreateSupport(
runtime: Pick<OrcaRuntimeService, 'getStructuredAgentSessionCreateSupport'>,
worktreeId: string,
agent: TuiAgent | undefined
): Promise<{ supported: boolean; reason?: 'agent' | 'remote' | 'wsl' } | null> {
): Promise<HostLaunchSupport | null> {
if (!agent || !REGISTERED_STRUCTURED_AGENTS.includes(agent)) {
return { supported: false, reason: 'agent' }
}
try {
return await runtime.getStructuredAgentSessionCreateSupport(`id:${worktreeId}`, agent)
// These launches have no chat tab to offer the install, so they keep the agent's own CLI.
return await runtime.getStructuredAgentSessionCreateSupport(`id:${worktreeId}`, agent, {
requireInstalledChatProgram: true
})
} catch {
return null
}
@@ -208,7 +217,7 @@ async function readStructuredCreateSupport(
export function downgradeAgentLaunchModeForHost(
receipt: AgentLaunchModeReceipt,
support: { supported: boolean; reason?: 'agent' | 'remote' | 'wsl' } | null,
support: HostLaunchSupport | null,
vocabulary: AgentLaunchModeVocabulary = DEFAULT_LAUNCH_VOCABULARY,
context?: AgentLaunchModeSettlement
): AgentLaunchModeReceipt {
@@ -109,7 +109,9 @@ describe('agent.launch with the real floating workspace resolver', () => {
expect(scope).toHaveBeenCalledExactlyOnceWith(selector)
if (structuredPreference) {
expect(createSupport).toHaveBeenCalledWith(`id:${FLOATING_TERMINAL_WORKTREE_ID}`, 'codex')
expect(createSupport).toHaveBeenCalledWith(`id:${FLOATING_TERMINAL_WORKTREE_ID}`, 'codex', {
requireInstalledChatProgram: true
})
expect(createStructuredSession).toHaveBeenCalledWith(
expect.objectContaining({ worktree: `id:${FLOATING_TERMINAL_WORKTREE_ID}` })
)
@@ -53,6 +53,29 @@ describe('a worker for a registered native-chat agent', () => {
})
})
it('starts a terminal worker when the chat program Orca installs is missing here', async () => {
const mode = decideWorkerStartMode({
params: { agent: 'antigravity' },
settings: STRUCTURED_PREFERENCE
})
// A desktop chat tab can offer the install; a worker cannot, so only it asks for an installed one.
const host = {
getStructuredAgentSessionCreateSupport: async (
_worktree: string,
_agent: string,
options?: { requireInstalledChatProgram?: boolean }
) =>
options?.requireInstalledChatProgram
? { supported: false, reason: 'chat-program' as const }
: { supported: true }
}
expect(await resolveWorkerStartModeOnHost(host, mode, 'wt-1', 'antigravity')).toMatchObject({
mode: 'terminal',
preferred: 'structured',
reason: 'chat_runtime_not_installed'
})
})
it('starts a terminal worker for an agent whose chat provider the user turned off', () => {
expect(
decideWorkerStartMode({
@@ -24,7 +24,13 @@ describe('host support evidence', () => {
reason: 'structured_support_unknown'
})
expect(unknown.detail).toContain('has not established')
expect(getStructuredAgentSessionCreateSupport).toHaveBeenCalledWith('id:workspace-1', 'claude')
expect(getStructuredAgentSessionCreateSupport).toHaveBeenCalledWith(
'id:workspace-1',
'claude',
{
requireInstalledChatProgram: true
}
)
getStructuredAgentSessionCreateSupport.mockResolvedValue({ supported: false })
const refusal = await resolveWorkerStartModeOnHost(runtime, mode, 'workspace-1', 'claude')
expect(refusal.reason).toBe('structured_unsupported_on_host')
@@ -168,7 +168,8 @@ describe('a fresh worker through the launch executor', () => {
expect(f.runtime.getStructuredAgentSessionCreateSupport).toHaveBeenCalledTimes(1)
expect(f.runtime.getStructuredAgentSessionCreateSupport).toHaveBeenCalledWith(
'id:wt_new',
'claude'
'claude',
{ requireInstalledChatProgram: true }
)
expect(f.stages).toEqual(['worktree_creating', 'worktree_created', 'terminal_creating'])
expect(f.failedStages).toEqual(['worktree_create', 'terminal_create', 'terminal_create'])
@@ -21,6 +21,7 @@ import {
resolveHostStructuredAgentLaunchEnvironment
} from '../structured-agent-launch-support'
import { structuredAgentRuntimeMetadata } from '../structured-agent-runtime-metadata'
import { structuredAgentChatProgramNeedsInstall } from '../structured-agent-availability'
import { structuredLaunchSeedOptions } from '../../../shared/native-chat-session-option-defaults'
import { hasPersistedStructuredAgentSessionStore as hasPersistedStructuredAgentSessionStoreOnDisk } from '../structured-agent-session-runtime'
import { ensureStructuredAgentSessionHostUnlessRefused } from '../structured-agent-session-host-refusal'
@@ -43,16 +44,26 @@ export abstract class OrcaRuntimeWithGetStructuredAgentSessionCreateSupport exte
RestoreStructuredAgentSessionTabsOnceMethods['restoreStructuredAgentSessionTabsOnce']
>
/** `requireInstalledChatProgram` is for launches with no chat tab to offer Orca's install. */
async getStructuredAgentSessionCreateSupport(
worktreeSelector: string,
agent: StructuredAgentId
): Promise<{ supported: boolean; reason?: 'agent' | 'remote' | 'wsl' }> {
return resolveHostStructuredAgentCreateSupport({
agent: StructuredAgentId,
options?: { requireInstalledChatProgram?: boolean }
): Promise<{ supported: boolean; reason?: 'agent' | 'remote' | 'wsl' | 'chat-program' }> {
const support = await resolveHostStructuredAgentCreateSupport({
agent,
worktreeSelector,
location: await this.resolveStructuredAgentSessionLocation(worktreeSelector),
runtime: this
})
if (
support.supported &&
options?.requireInstalledChatProgram &&
(await structuredAgentChatProgramNeedsInstall(agent, this))
) {
return { supported: false, reason: 'chat-program' }
}
return support
}
/** Use this runtime's registered definition to pin account home; reads/create must not install the host just to inspect it. */
@@ -12,7 +12,10 @@ import {
observeStructuredAgentLaunch,
resolveHostStructuredAgentLaunchEnvironment
} from './structured-agent-launch-support'
import { STRUCTURED_AGENT_RUNTIME_METADATA } from './structured-agent-runtime-metadata'
import {
STRUCTURED_AGENT_RUNTIME_METADATA,
structuredAgentRuntimeMetadata
} from './structured-agent-runtime-metadata'
import type { StructuredAgentRuntimeMetadata } from './structured-agent-runtime-registration-types'
import { recaptureHostShellEnvironment } from './structured-agent-shell-environment'
import type { RuntimeStore } from './runtime-store-contract'
@@ -89,6 +92,18 @@ async function observeChatProgram(
return chatProgram.installable?.() ? 'installable' : 'missing'
}
/** True when a chat start would fail until Orca installs the agent's chat program on this host. */
export async function structuredAgentChatProgramNeedsInstall(
agent: string,
runtime: HealthRuntime
): Promise<boolean> {
const metadata = structuredAgentRuntimeMetadata(agent)
return Boolean(
metadata?.chatProgram?.installable &&
(await observeChatProgram(metadata, runtime)) === 'installable'
)
}
/** Refresh shell facts on install, settings or agent Refresh; retain health without readers for immediate later subscription. */
export function installStructuredAgentAvailability(
store: Pick<RuntimeStore, 'onSettingsChanged'> | null,
@@ -53,7 +53,7 @@ type TestLocation = {
type SupportResult = {
supported: boolean
reason?: 'agent' | 'remote' | 'wsl'
reason?: 'agent' | 'remote' | 'wsl' | 'chat-program'
}
function createRuntime(location: TestLocation): OrcaRuntimeService {
+1
View File
@@ -137,6 +137,7 @@ export type AgentLaunchModeReason =
| 'wsl_execution_runtime'
| 'codex_on_windows'
| 'structured_unsupported_on_host'
| 'chat_runtime_not_installed'
/** Restates `WorkerStartModeReceipt` in surface-neutral terms so orchestration's receipt and a
* mobile or renderer launch report the same vocabulary. */