Files
orca/src/main/runtime/runtime-worktree-agent-startup.ts
T
Brennan Benson 5468de16bc fix(orchestration): offer the worker brief to the one launch carry rule
Round-1 review fixes. The worker brief no longer forces its own sensitive
launch file past the host checks. Worker start offers the brief as an ordinary
startup prompt with main's paste (`once-agent-runs`): it rides the agent's
launch line where the line carries it exactly, and is otherwise pasted once the
agent is ready, as main pasted it, so a host that cannot take a launch file no
longer fails the start. With no sensitive file, the agent's hook reports the
brief itself, which status readers compact to its task as on main.

- Created worktrees are agent-first again, as on main: the startup terminal
  keeps the handle the brief names, setup and default tabs follow it, and a
  wait-for-setup sequence still holds the agent until setup succeeds.
- The brief names the CLI command the spawn's own WSL decision will give the
  terminal.
- A launch the shell reports finished, with no agent in front, fails and tears
  down the start. Where hooks are off or never reach the pane, the launch is
  judged by main's evidence: a title turn-start edge, or the agent holding its
  terminal.
2026-10-02 08:00:32 -07:00

212 lines
7.6 KiB
TypeScript

import { agentStartedTelemetry } from '../agent-launch/agent-started-telemetry'
import type { AgentLaunchPreferences } from '../../shared/agent-session-host-authority'
import type { Repo } from '../../shared/repo-types'
import type { TuiAgent } from '../../shared/tui-agent'
import type { WorktreeStartupLaunch } from '../../shared/worktree/launch-types'
import { repoIsRemote } from '../../shared/agent-launch-remote'
import { getRepoSshConnectionId } from '../../shared/execution-host'
import { isTuiAgent } from '../../shared/tui-agent-config'
import { isTuiAgentEnabled, pickTuiAgent } from '../../shared/tui-agent-selection'
import { resolveAgentStartupPlanInputs } from '../../shared/agent-startup-plan-inputs'
import {
buildAgentDraftLaunchPlan,
buildAgentStartupPlan,
planLaunchPrompt,
type AgentStartupPlan
} from '../../shared/tui-agent-startup'
import type { LaunchFile } from '../../shared/launch-prompt-file'
import type { LaunchPromptPaste } from '../../shared/launch-prompt-carry'
import { thisOrcaLaunchHost } from './this-orca-launch-host'
import {
detectInstalledAgentsWithShellPathHydration,
detectRemoteAgents
} from '../preflight/agent-detection'
import type { RuntimeStore } from './runtime-store-contract'
export type WorktreeStartupDraftPaste = { agent: TuiAgent; content: string }
export type WorktreeStartupFollowup = { expectedProcess: string; prompt: string }
/** A fresh agent the host builds always carries its `agent_started` record; dropping it fails to compile. */
type AttributedWorktreeStartupLaunch = WorktreeStartupLaunch & {
telemetry: NonNullable<WorktreeStartupLaunch['telemetry']>
}
type StartupEnvironment = {
repo: Repo
settings: ReturnType<RuntimeStore['getSettings']>
getLaunchPlatform: () => NodeJS.Platform
/** Replaces the configured arguments for this launch; `null` means none. */
agentArgs?: string | null
/** Caller-supplied telemetry attribution, validated leniently at the host boundary. */
launchSource?: string
}
export async function buildWorktreeStartupForDraft(
environment: StartupEnvironment & { draft: string; requestedAgent?: TuiAgent }
): Promise<{
agent: TuiAgent
startup: AttributedWorktreeStartupLaunch
draftPaste?: WorktreeStartupDraftPaste
} | null> {
const content = environment.draft.trim()
if (!content) {
return null
}
const { repo, settings } = environment
const preferredAgent = environment.requestedAgent ?? settings.defaultTuiAgent
// Why: `blank` is an explicit shell-only preference, so linked drafts must not auto-pick an agent.
if (preferredAgent === 'blank') {
return null
}
let agent =
isTuiAgent(preferredAgent) && isTuiAgentEnabled(preferredAgent, settings.disabledTuiAgents)
? preferredAgent
: null
if (!agent) {
let detected: string[] = []
// Why: detection has to run on the machine that will run the agent, and SSH ownership has two
// spellings — the raw field probes this client for an `executionHostId: 'ssh:*'`-only repo.
const sshConnectionId = getRepoSshConnectionId(repo)
try {
// Why: startup-draft fallback can run from sparse runtime launch envs too.
detected = sshConnectionId
? await detectRemoteAgents({ connectionId: sshConnectionId })
: await detectInstalledAgentsWithShellPathHydration()
} catch {
detected = []
}
agent = pickTuiAgent(null, detected.filter(isTuiAgent), settings.disabledTuiAgents)
}
if (!agent) {
return null
}
const launchArgs = resolveAgentStartupPlanInputs({
agent,
settings,
platform: environment.getLaunchPlatform(),
isRemote: repoIsRemote(repo),
...(environment.agentArgs !== undefined ? { agentArgs: environment.agentArgs } : {})
})
const telemetry = agentStartedTelemetry(agent, environment.launchSource)
const draftPlan = buildAgentDraftLaunchPlan({ ...launchArgs, draft: content })
if (draftPlan) {
return {
agent,
startup: {
command: draftPlan.launchCommand,
launchConfig: draftPlan.launchConfig,
...(draftPlan.startupCommandDelivery
? { startupCommandDelivery: draftPlan.startupCommandDelivery }
: {}),
...(draftPlan.env ? { env: draftPlan.env } : {}),
telemetry
}
}
}
const startupPlan = buildAgentStartupPlan({
...launchArgs,
prompt: '',
allowEmptyPromptLaunch: true
})
if (!startupPlan) {
return null
}
return {
agent,
startup: {
command: startupPlan.launchCommand,
launchConfig: startupPlan.launchConfig,
...(startupPlan.startupCommandDelivery
? { startupCommandDelivery: startupPlan.startupCommandDelivery }
: {}),
...(startupPlan.env ? { env: startupPlan.env } : {}),
telemetry
},
draftPaste: { agent, content }
}
}
export function buildWorktreeStartupForAgent(
environment: StartupEnvironment & {
agent: TuiAgent
prompt?: string
launchPreferences?: AgentLaunchPreferences
toSessionOptions: (
preferences?: AgentLaunchPreferences
) => Parameters<typeof buildAgentStartupPlan>[0]['sessionOptions'] | undefined
/** Set by a caller that pastes an uncarried prompt itself; reports whether it rode the command,
* and no follow-up is returned for it to paste twice. */
onPromptCarry?: (carried: boolean) => void
/** The caller's paste for what the line leaves; #24257's guarded paste by default. */
promptPaste?: LaunchPromptPaste
}
): {
agent: TuiAgent
startup: AttributedWorktreeStartupLaunch
followup?: WorktreeStartupFollowup
} {
const { agent, repo, settings } = environment
if (!isTuiAgentEnabled(agent, settings.disabledTuiAgents)) {
throw new Error('Selected agent is disabled. Choose an enabled agent before creating.')
}
const planInputs = resolveAgentStartupPlanInputs({
agent,
settings,
platform: environment.getLaunchPlatform(),
isRemote: repoIsRemote(repo),
...(environment.agentArgs !== undefined ? { agentArgs: environment.agentArgs } : {}),
sessionOptions: environment.toSessionOptions(environment.launchPreferences)
})
const planned = planLaunchPrompt({
...planInputs,
prompt: environment.prompt ?? '',
host: thisOrcaLaunchHost({
isRemote: repoIsRemote(repo),
launchPlatform: environment.getLaunchPlatform(),
settings
}),
// The caller, or else the host's follow-up, pastes what the line leaves once the agent runs.
paste: environment.promptPaste ?? 'when-host-proves-agent'
})
if (!planned) {
throw new Error(`Could not build launch command for ${agent}.`)
}
let startupPlan: AgentStartupPlan
let launchFile: LaunchFile | undefined
let followup: WorktreeStartupFollowup | undefined
switch (planned.carry) {
case 'none':
startupPlan = planned.plan
break
case 'on-line':
case 'launch-file':
startupPlan = planned.plan
launchFile = planned.carry === 'launch-file' ? planned.launchFile : undefined
environment.onPromptCarry?.(true)
break
case 'paste-after-ready':
startupPlan = planned.cleanPlan
if (environment.onPromptCarry) {
environment.onPromptCarry(false)
} else {
followup = { expectedProcess: startupPlan.expectedProcess, prompt: planned.text }
}
break
}
return {
agent,
startup: {
command: startupPlan.launchCommand,
launchConfig: startupPlan.launchConfig,
...(startupPlan.startupCommandDelivery
? { startupCommandDelivery: startupPlan.startupCommandDelivery }
: {}),
...(startupPlan.env ? { env: startupPlan.env } : {}),
...(launchFile ? { launchFile } : {}),
telemetry: agentStartedTelemetry(agent, environment.launchSource)
},
...(followup ? { followup } : {})
}
}