refactor(agent-launch): make the launch-mode decision surface-neutral

`decideWorkerStartMode` was the only shared answer to "structured chat session
or terminal agent?", but it lived in an orchestration-named module and spoke
orchestration's vocabulary, so the other launch surfaces could not call it.
Move the decision to `main/agent-launch/agent-launch-mode` unchanged and leave
`orchestration-worker-start-mode` as the adapter that supplies the noun.

A worker is not a special kind of launch; it is the same launch with a dispatch
attached. Naming the receipt's subject is the only thing orchestration actually
contributed, so that is the only thing the adapter keeps: "worker" in both
sentences, plus the `--terminal` wording, which reads as nonsense anywhere a
`--terminal` flag does not exist. Both are pinned, because they are asserted.

No behavior change. The receipts are byte-identical for every reachable case,
proven by running the new pin against both implementations.

Also pins the wording, which nothing was holding. The existing suites assert
`toContain` fragments ('terminal agent', 'cannot create') and the CLI suite
asserts a receipt handed to it by a mock rather than one this code produced;
all six files stayed green against a deliberately corrupted vocabulary. A
dispatch receipt is the only place a structured-to-terminal downgrade explains
itself, so the whole sentence is the contract, not a fragment of it.
This commit is contained in:
Merge Sim
2026-09-09 17:19:13 -07:00
parent aac38d698f
commit f34d08a452
3 changed files with 424 additions and 172 deletions
+248
View File
@@ -0,0 +1,248 @@
/**
* Which surface a launch gets — a structured chat session or a terminal agent — decided from the
* user's own settings and the executing host's answer.
*
* No caller passes a mode. If the user's default is that a new agent tab opens as a structured
* native chat, then every launch is one: an orchestration worker, a mobile create, a CLI create,
* a renderer tab. That default is a preference rather than a demand, so a launch it cannot apply
* to falls back to a PTY terminal and the receipt says which mode ran and why — a routine launch
* must never fail because the user happens to have a chat preference on.
*
* The settings default and the per-launch feasibility both come from
* `shared/structured-native-chat-launch-route`. This module supplies placement facts and formats
* the receipt; it does not own a second feasibility policy.
*
* Callers differ only in what they call the thing being started, so the receipt's noun is
* parameterized. Orchestration says "worker" because its receipts are read alongside dispatch
* records; every other surface says "chat session" / "terminal agent".
*/
import type { GlobalSettings } from '../../shared/global-settings-types'
import { RUNTIME_CAPABILITIES } from '../../shared/protocol-version'
import {
prefersStructuredNativeChatByDefault,
resolveStructuredNativeChatSupport,
type NativeChatDefaultSettings,
type StructuredNativeChatBlocker
} from '../../shared/structured-native-chat-launch-route'
import type { TuiAgent } from '../../shared/tui-agent'
import { hasExplicitTuiLaunchCustomization } from '../../shared/tui-agent-launch-customization'
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
export type AgentLaunchMode = 'structured' | 'terminal'
export type AgentLaunchModeReason =
| 'user_default'
| 'remote_execution_host'
| 'reused_terminal'
| 'agent_without_structured_session'
| 'tui_launch_customization'
| 'structured_sessions_unavailable'
| 'structured_support_unknown'
| 'wsl_execution_runtime'
| 'codex_on_windows'
| 'structured_unsupported_on_host'
export type AgentLaunchModeReceipt = {
/** The mode the launch actually ran in. */
mode: AgentLaunchMode
/** The user's settings default for a new agent tab. */
preferred: AgentLaunchMode
reason: AgentLaunchModeReason
/** One sentence, always present, so a fallback is never silent. */
detail: string
}
/** What this caller calls the thing it is starting, so one decision serves every surface without
* a receipt reading "worker" on a phone. */
export type AgentLaunchModeVocabulary = {
/** e.g. 'a structured chat session worker' */
structured: string
/** e.g. 'a terminal agent worker' */
terminal: string
/** Per-reason wording a surface states differently. Orchestration names the `--terminal` flag
* in its reused-terminal detail, which would be meaningless in a phone's receipt. */
detailOverrides?: Partial<Record<Exclude<AgentLaunchModeReason, 'user_default'>, string>>
}
export const DEFAULT_LAUNCH_VOCABULARY: AgentLaunchModeVocabulary = {
structured: 'a structured chat session',
terminal: 'a terminal agent'
}
export type AgentLaunchModeSettings = Partial<
NativeChatDefaultSettings &
Pick<GlobalSettings, 'agentCmdOverrides' | 'agentDefaultArgs' | 'agentDefaultEnv'>
>
/** The placement facts the decision reads. `worktree`, `model` and `effort` are deliberately not
* here: a structured launch honours all three, and a placement flag must never imply a mode. */
export type AgentLaunchModePlacement = {
agent?: string
/** A connected execution server; absent means local. */
on?: string
/** An existing terminal being reused. */
terminal?: string
}
const DOWNGRADE_DETAIL: Record<Exclude<AgentLaunchModeReason, 'user_default'>, string> = {
remote_execution_host: 'this launch runs on a remote execution host',
reused_terminal: 'it reuses a running terminal agent',
agent_without_structured_session: 'this agent has no structured session',
tui_launch_customization:
'this agent has a custom launch command, arguments or environment that only a terminal applies',
structured_sessions_unavailable: 'this runtime does not support structured agent sessions',
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'
}
const BLOCKER_REASON: Record<
StructuredNativeChatBlocker,
Exclude<AgentLaunchModeReason, 'user_default'>
> = {
'reused-terminal': 'reused_terminal',
'agent-without-structured-session': 'agent_without_structured_session',
'draft-prompt': 'structured_unsupported_on_host',
'floating-workspace': 'structured_unsupported_on_host',
'tui-launch-customization': 'tui_launch_customization',
'remote-execution-host': 'remote_execution_host',
'project-runtime': 'wsl_execution_runtime',
'runtime-capability': 'structured_sessions_unavailable',
'runtime-capability-unknown': 'structured_support_unknown'
}
/** The host's own create-support verdict (`agentSession.createSupport`) in this vocabulary. */
const HOST_SUPPORT_REASON: Record<
'agent' | 'remote' | 'wsl',
Exclude<AgentLaunchModeReason, 'user_default'>
> = {
agent: 'structured_unsupported_on_host',
remote: 'remote_execution_host',
wsl: 'wsl_execution_runtime'
}
/**
* First half of the decision: the user's default, plus every feasibility fact knowable before a
* workspace is resolved.
*/
export function decideAgentLaunchMode(args: {
placement: AgentLaunchModePlacement
settings: AgentLaunchModeSettings | null | undefined
vocabulary?: AgentLaunchModeVocabulary
}): AgentLaunchModeReceipt {
const { placement, settings } = args
const vocabulary = args.vocabulary ?? DEFAULT_LAUNCH_VOCABULARY
if (!prefersStructuredNativeChatByDefault(settings)) {
return {
mode: 'terminal',
preferred: 'terminal',
reason: 'user_default',
detail: `Started ${vocabulary.terminal}, the default for new agent tabs in your settings.`
}
}
const agent = placement.agent as TuiAgent
const support = resolveStructuredNativeChatSupport({
agent,
executionHostId: placement.on ? `runtime:${placement.on}` : 'local',
reusesTerminal: Boolean(placement.terminal),
hostCapabilities: RUNTIME_CAPABILITIES,
// A resolved managed worktree or folder workspace is never a floating terminal. WSL is left to
// the executing host's own create-support probe, which reads the resolved workspace rather
// than guessing from a client-side project runtime.
requiresTuiLaunchCustomization: hasExplicitTuiLaunchCustomization(settings, agent)
})
if (!support.supported) {
return downgraded(BLOCKER_REASON[support.blocker], vocabulary)
}
return {
mode: 'structured',
preferred: 'structured',
reason: 'user_default',
detail: `Started ${vocabulary.structured}, the default for new agent tabs in your settings.`
}
}
/**
* Second half, once the workspace is resolved: the host that will run the agent answers whether it
* can create a structured session there at all. Asked before anything is created, so a refusal
* becomes a terminal agent rather than a failed launch.
*/
export async function resolveAgentLaunchModeOnHost(
runtime: Pick<OrcaRuntimeService, 'getStructuredAgentSessionCreateSupport'>,
receipt: AgentLaunchModeReceipt,
worktreeId: string | undefined,
agent: TuiAgent | undefined,
vocabulary: AgentLaunchModeVocabulary = DEFAULT_LAUNCH_VOCABULARY
): Promise<AgentLaunchModeReceipt> {
if (receipt.mode !== 'structured' || !worktreeId) {
return receipt
}
return downgradeAgentLaunchModeForHost(
receipt,
await readStructuredCreateSupport(runtime, worktreeId, agent),
vocabulary
)
}
/** A host that cannot answer has not proved it can create one, so the launch stays a PTY agent. */
async function readStructuredCreateSupport(
runtime: Pick<OrcaRuntimeService, 'getStructuredAgentSessionCreateSupport'>,
worktreeId: string,
agent: TuiAgent | undefined
): Promise<{ supported: boolean; reason?: 'agent' | 'remote' | 'wsl' } | null> {
if (agent !== 'claude' && agent !== 'codex') {
return { supported: false, reason: 'agent' }
}
try {
return await runtime.getStructuredAgentSessionCreateSupport(`id:${worktreeId}`, agent)
} catch {
return null
}
}
/**
* Applies the executing host's `agentSession.createSupport` answer, which is the authority on WSL,
* remoteness and the Windows process-start-time gate for the resolved workspace.
*/
export function downgradeAgentLaunchModeForHost(
receipt: AgentLaunchModeReceipt,
support: { supported: boolean; reason?: 'agent' | 'remote' | 'wsl' } | null,
vocabulary: AgentLaunchModeVocabulary = DEFAULT_LAUNCH_VOCABULARY
): AgentLaunchModeReceipt {
if (receipt.mode !== 'structured' || support?.supported) {
return receipt
}
if (support === null) {
return downgraded(BLOCKER_REASON['runtime-capability-unknown'], vocabulary)
}
return downgraded(
support.reason ? HOST_SUPPORT_REASON[support.reason] : 'structured_unsupported_on_host',
vocabulary
)
}
function downgraded(
reason: Exclude<AgentLaunchModeReason, 'user_default'>,
vocabulary: AgentLaunchModeVocabulary
): AgentLaunchModeReceipt {
const why = vocabulary.detailOverrides?.[reason] ?? DOWNGRADE_DETAIL[reason]
return {
mode: 'terminal',
preferred: 'structured',
reason,
detail: `Your default is a structured chat session, but ${why}; started ${vocabulary.terminal} instead.`
}
}
/** The store can be missing on a runtime that never opened one; that reads as no preference. */
export function readAgentLaunchModeSettings(
runtime: Pick<OrcaRuntimeService, 'getClientSettings'>
): AgentLaunchModeSettings | null {
try {
return runtime.getClientSettings()
} catch {
return null
}
}
@@ -1,59 +1,41 @@
/**
* Which kind of worker `orchestration.workerStart` starts, decided from the user's own settings.
* `orchestration.workerStart`'s view of the shared launch-mode decision.
*
* There is no `--structured` flag: if the user's default is that a new agent tab opens as a
* structured native chat, an orchestration worker is one too. That default is a preference, not a
* demand, so a dispatch it cannot apply to falls back to an ordinary PTY terminal worker and the
* receipt says which mode ran and why — a routine `worker-start` must never fail because the user
* happens to have a chat preference on.
*
* The settings default and the per-launch feasibility both come from
* `shared/structured-native-chat-launch-route`, the same module the renderer's
* `resolveAgentLaunchRoute` uses. This adapter supplies placement facts and formats the receipt;
* it does not own a second feasibility policy.
* The decision itself lives in `main/agent-launch/agent-launch-mode`, which every launch surface
* shares — a worker is not a special kind of launch, it is the same launch with a dispatch
* attached. All this module contributes is the noun orchestration puts in its receipts ("worker")
* and the `--terminal` wording, so a dispatch receipt reads the way it always has.
*/
import type { GlobalSettings } from '../../../../shared/global-settings-types'
import { RUNTIME_CAPABILITIES } from '../../../../shared/protocol-version'
import {
prefersStructuredNativeChatByDefault,
resolveStructuredNativeChatSupport,
type NativeChatDefaultSettings,
type StructuredNativeChatBlocker
} from '../../../../shared/structured-native-chat-launch-route'
decideAgentLaunchMode,
downgradeAgentLaunchModeForHost,
readAgentLaunchModeSettings,
resolveAgentLaunchModeOnHost,
type AgentLaunchMode,
type AgentLaunchModeReason,
type AgentLaunchModeReceipt,
type AgentLaunchModeSettings,
type AgentLaunchModeVocabulary
} from '../../../agent-launch/agent-launch-mode'
import type { TuiAgent } from '../../../../shared/tui-agent'
import { hasExplicitTuiLaunchCustomization } from '../../../../shared/tui-agent-launch-customization'
import type { OrcaRuntimeService } from '../../orca-runtime'
export type WorkerStartMode = 'structured' | 'terminal'
export type WorkerStartMode = AgentLaunchMode
export type WorkerStartModeReason = AgentLaunchModeReason
export type WorkerStartModeReceipt = AgentLaunchModeReceipt
export type WorkerStartModeReason =
| 'user_default'
| 'remote_execution_host'
| 'reused_terminal'
| 'agent_without_structured_session'
| 'tui_launch_customization'
| 'structured_sessions_unavailable'
| 'structured_support_unknown'
| 'wsl_execution_runtime'
| 'codex_on_windows'
| 'structured_unsupported_on_host'
export type WorkerStartModeReceipt = {
/** The mode the worker actually started in. */
mode: WorkerStartMode
/** The user's settings default for a new agent tab. */
preferred: WorkerStartMode
reason: WorkerStartModeReason
/** One sentence, always present, so a fallback is never silent. */
detail: string
/** Orchestration's receipts are read next to dispatch records, so they name the worker and the
* flag that reused a terminal. Pinned here because the exact strings are asserted. */
export const WORKER_START_VOCABULARY: AgentLaunchModeVocabulary = {
structured: 'a structured chat session worker',
terminal: 'a terminal agent worker',
detailOverrides: {
remote_execution_host: 'this worker runs on a remote execution host',
reused_terminal: '--terminal reuses a running terminal agent'
}
}
type WorkerStartModeSettings = Partial<
NativeChatDefaultSettings &
Pick<GlobalSettings, 'agentCmdOverrides' | 'agentDefaultArgs' | 'agentDefaultEnv'>
>
/** The placement options that exist only on `worker-start`. `worktree`, `model` and `effort` are
* listed but no longer read: a structured worker honours all three, and naming them here keeps
* the set of options this decision has considered visible. */
@@ -66,153 +48,35 @@ type WorkerStartModePlacement = {
effort?: string
}
const DOWNGRADE_DETAIL: Record<Exclude<WorkerStartModeReason, 'user_default'>, string> = {
remote_execution_host: 'this worker runs on a remote execution host',
reused_terminal: '--terminal reuses a running terminal agent',
agent_without_structured_session: 'this agent has no structured session',
tui_launch_customization:
'this agent has a custom launch command, arguments or environment that only a terminal applies',
structured_sessions_unavailable: 'this runtime does not support structured agent sessions',
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'
}
const BLOCKER_REASON: Record<
StructuredNativeChatBlocker,
Exclude<WorkerStartModeReason, 'user_default'>
> = {
'reused-terminal': 'reused_terminal',
'agent-without-structured-session': 'agent_without_structured_session',
'draft-prompt': 'structured_unsupported_on_host',
'floating-workspace': 'structured_unsupported_on_host',
'tui-launch-customization': 'tui_launch_customization',
'remote-execution-host': 'remote_execution_host',
'project-runtime': 'wsl_execution_runtime',
'runtime-capability': 'structured_sessions_unavailable',
'runtime-capability-unknown': 'structured_support_unknown'
}
/** The host's own create-support verdict (`agentSession.createSupport`) in this vocabulary. */
const HOST_SUPPORT_REASON: Record<
'agent' | 'remote' | 'wsl',
Exclude<WorkerStartModeReason, 'user_default'>
> = {
agent: 'structured_unsupported_on_host',
remote: 'remote_execution_host',
wsl: 'wsl_execution_runtime'
}
export function decideWorkerStartMode(args: {
params: WorkerStartModePlacement
settings: WorkerStartModeSettings | null | undefined
settings: AgentLaunchModeSettings | null | undefined
}): WorkerStartModeReceipt {
const { params, settings } = args
if (!prefersStructuredNativeChatByDefault(settings)) {
return {
mode: 'terminal',
preferred: 'terminal',
reason: 'user_default',
detail: 'Started a terminal agent worker, the default for new agent tabs in your settings.'
}
}
const agent = params.agent as TuiAgent
const support = resolveStructuredNativeChatSupport({
agent,
executionHostId: params.on ? `runtime:${params.on}` : 'local',
reusesTerminal: Boolean(params.terminal),
hostCapabilities: RUNTIME_CAPABILITIES,
// Orchestration resolves a managed worktree or folder workspace; a floating terminal is never
// a worker placement. WSL is left to the executing host's own create-support probe, which
// reads the resolved workspace rather than guessing from a client-side project runtime.
requiresTuiLaunchCustomization: hasExplicitTuiLaunchCustomization(settings, agent)
return decideAgentLaunchMode({
placement: args.params,
settings: args.settings,
vocabulary: WORKER_START_VOCABULARY
})
if (!support.supported) {
return downgraded(BLOCKER_REASON[support.blocker])
}
return {
mode: 'structured',
preferred: 'structured',
reason: 'user_default',
detail:
'Started a structured chat session worker, the default for new agent tabs in your settings.'
}
}
/**
* Second half of the decision, once the worktree is resolved: the host that will run the worker
* answers whether it can create a structured session there at all. Asked before anything is
* created, so a refusal becomes a terminal worker rather than a failed start.
*/
export async function resolveWorkerStartModeOnHost(
runtime: Pick<OrcaRuntimeService, 'getStructuredAgentSessionCreateSupport'>,
mode: WorkerStartModeReceipt,
worktreeId: string | undefined,
agent: TuiAgent | undefined
): Promise<WorkerStartModeReceipt> {
if (mode.mode !== 'structured' || !worktreeId) {
return mode
}
return downgradeWorkerStartModeForHost(
mode,
await readStructuredCreateSupport(runtime, worktreeId, agent)
)
return resolveAgentLaunchModeOnHost(runtime, mode, worktreeId, agent, WORKER_START_VOCABULARY)
}
/** A host that cannot answer has not proved it can create one, so the worker stays a PTY agent. */
async function readStructuredCreateSupport(
runtime: Pick<OrcaRuntimeService, 'getStructuredAgentSessionCreateSupport'>,
worktreeId: string,
agent: TuiAgent | undefined
): Promise<{ supported: boolean; reason?: 'agent' | 'remote' | 'wsl' } | null> {
if (agent !== 'claude' && agent !== 'codex') {
return { supported: false, reason: 'agent' }
}
try {
return await runtime.getStructuredAgentSessionCreateSupport(`id:${worktreeId}`, agent)
} catch {
return null
}
}
/**
* Applies the executing host's `agentSession.createSupport` answer, which is the authority on WSL,
* remoteness and the Windows process-start-time gate for the resolved workspace.
*/
export function downgradeWorkerStartModeForHost(
receipt: WorkerStartModeReceipt,
support: { supported: boolean; reason?: 'agent' | 'remote' | 'wsl' } | null
): WorkerStartModeReceipt {
if (receipt.mode !== 'structured' || support?.supported) {
return receipt
}
if (support === null) {
return downgraded(BLOCKER_REASON['runtime-capability-unknown'])
}
return downgraded(
support.reason ? HOST_SUPPORT_REASON[support.reason] : 'structured_unsupported_on_host'
)
return downgradeAgentLaunchModeForHost(receipt, support, WORKER_START_VOCABULARY)
}
function downgraded(
reason: Exclude<WorkerStartModeReason, 'user_default'>
): WorkerStartModeReceipt {
return {
mode: 'terminal',
preferred: 'structured',
reason,
detail: `Your default is a structured chat session, but ${DOWNGRADE_DETAIL[reason]}; started a terminal agent worker instead.`
}
}
/** The store can be missing on a runtime that never opened one; that reads as no preference. */
export function readWorkerStartModeSettings(
runtime: Pick<OrcaRuntimeService, 'getClientSettings'>
): WorkerStartModeSettings | null {
try {
return runtime.getClientSettings()
} catch {
return null
}
): AgentLaunchModeSettings | null {
return readAgentLaunchModeSettings(runtime)
}
@@ -0,0 +1,140 @@
/**
* The exact sentences `orchestration.workerStart` puts in its mode receipt.
*
* These were unpinned when the decision moved to `main/agent-launch/agent-launch-mode`: the
* existing suites assert `toContain` fragments ('terminal agent', 'cannot create'), and the CLI
* suite asserts a receipt handed to it by a mock rather than one this code produced. Every one of
* them stayed green against a deliberately corrupted vocabulary, so nothing was actually holding
* the wording. A dispatch receipt is the only place a structured→terminal downgrade explains
* itself, so the whole sentence is the contract, not a fragment of it.
*/
import { describe, expect, it } from 'vitest'
import {
decideWorkerStartMode,
downgradeWorkerStartModeForHost,
type WorkerStartModeReceipt
} from './orchestration-worker-start-mode'
const STRUCTURED_PREFERENCE = {
experimentalNativeChat: true,
experimentalStructuredNativeChat: true,
openAgentTabsInChatByDefault: true
} as const
function structuredReceipt(): WorkerStartModeReceipt {
const receipt = decideWorkerStartMode({
params: { agent: 'claude' },
settings: STRUCTURED_PREFERENCE
})
expect(receipt.mode).toBe('structured')
return receipt
}
function downgradeSentence(why: string): string {
return `Your default is a structured chat session, but ${why}; started a terminal agent worker instead.`
}
describe('worker-start mode receipt wording', () => {
it('states the settings default when the user has no structured preference', () => {
expect(decideWorkerStartMode({ params: { agent: 'claude' }, settings: null })).toEqual({
mode: 'terminal',
preferred: 'terminal',
reason: 'user_default',
detail: 'Started a terminal agent worker, the default for new agent tabs in your settings.'
})
})
it('states the settings default when the launch is structured', () => {
expect(structuredReceipt()).toEqual({
mode: 'structured',
preferred: 'structured',
reason: 'user_default',
detail:
'Started a structured chat session worker, the default for new agent tabs in your settings.'
})
})
it.each([
[
'remote execution host',
{ agent: 'claude', on: 'server-1' },
'remote_execution_host',
'this worker runs on a remote execution host'
],
[
'reused terminal',
{ agent: 'claude', terminal: 'term_1' },
'reused_terminal',
'--terminal reuses a running terminal agent'
],
[
'agent with no structured session',
{ agent: 'grok' },
'agent_without_structured_session',
'this agent has no structured session'
]
])('names the %s downgrade in full', (_label, params, reason, why) => {
expect(decideWorkerStartMode({ params, settings: STRUCTURED_PREFERENCE })).toEqual({
mode: 'terminal',
preferred: 'structured',
reason,
detail: downgradeSentence(why)
})
})
it('names a custom TUI launch as the downgrade', () => {
expect(
decideWorkerStartMode({
params: { agent: 'claude' },
settings: { ...STRUCTURED_PREFERENCE, agentDefaultArgs: { claude: '--custom' } }
})
).toEqual({
mode: 'terminal',
preferred: 'structured',
reason: 'tui_launch_customization',
detail: downgradeSentence(
'this agent has a custom launch command, arguments or environment that only a terminal applies'
)
})
})
it.each([
[
'an unanswered host',
null,
'structured_support_unknown',
'the execution host has not established structured session support'
],
[
'a host refusal with no reason',
{ supported: false },
'structured_unsupported_on_host',
'the execution host cannot create one here'
],
[
'a WSL workspace',
{ supported: false, reason: 'wsl' as const },
'wsl_execution_runtime',
'this workspace runs under WSL'
],
[
'a remote workspace',
{ supported: false, reason: 'remote' as const },
'remote_execution_host',
'this worker runs on a remote execution host'
]
])('names %s in full', (_label, support, reason, why) => {
expect(downgradeWorkerStartModeForHost(structuredReceipt(), support)).toEqual({
mode: 'terminal',
preferred: 'structured',
reason,
detail: downgradeSentence(why)
})
})
it('leaves a settled terminal receipt untouched', () => {
const terminal = decideWorkerStartMode({ params: { agent: 'claude' }, settings: null })
expect(downgradeWorkerStartModeForHost(terminal, null)).toEqual(terminal)
})
})