Files
orca/src/shared/inline-env-command-prefix.ts
T
Brennan Benson be45fc4c75 fix(structured-chat): prove a resumed Codex TUI off-screen
Returning a Codex session from structured chat to its terminal always failed
with "did not prove the expected Codex rollout". Adoption stops the pane's
Codex, so `launchAdoptedStructuredTui` typed a resume command into a bare shell
and went straight to the proof, which pasted `/status` and scraped the tail for
15s. The probe landed before Codex was up, and it read kittyKeyboardFlags from
the mode tracker before the new process negotiated, submitting a bare CR.

A `tui-idle` wait would not fix it: that resolves on a shell prompt preview and
on the pre-adoption `lastAgentStatus`. Nothing on screen separates a ready shell
from a ready Codex.

The resumed CLI can answer for itself. Orca already installs Codex SessionStart
hooks, and the payload carries session_id, transcript_path and source=resume.
The hook now also echoes back a one-shot nonce Orca stamps into the single
resume invocation it types, so a stale SessionStart from an earlier process in
the same pane cannot pass for the new one. The nonce is a new env var rather
than `launchToken`, which is pane-lifetime identity that authority attestation
matches against. Nothing is pasted at the pane any more, so the submit-key
question disappears with the probe.

An installed hook script predating this omits the nonce; pane + thread still
bind, so that degrades instead of stranding the path. With agent status hooks
off there is no proof to wait for, and the launch says so up front.
2026-08-19 17:10:38 -07:00

47 lines
2.0 KiB
TypeScript

// Env assignments for a command Orca TYPES into a shell it already owns, rather
// than one it spawns with an env map. The adopted-Codex return path has no spawn
// to attach env to: the pane is a live shell and the resume command is keystrokes.
import {
quoteStartupArg,
resolveStartupShell,
type AgentStartupShell
} from './tui-agent-startup-shell'
export function buildInlineEnvCommandPrefix(args: {
env: Record<string, string>
platform: NodeJS.Platform
shell?: AgentStartupShell
}): string {
const entries = Object.entries(args.env)
if (entries.length === 0) {
return ''
}
const shell = resolveStartupShell(args.platform, args.shell)
const quote = (value: string): string => quoteStartupArg(value, shell)
if (shell === 'powershell') {
return `${entries.map(([name, value]) => `$env:${name}=${quote(value)}`).join('; ')}; `
}
if (shell === 'cmd') {
// Why: `set` here outlives the command, unlike the POSIX one-shot form. Safe
// because every caller overwrites the same names on its next invocation, so a
// leftover value can only ever fail a match — never satisfy a fresh one.
return `${entries.map(([name, value]) => `set ${quote(`${name}=${value}`)}`).join(' && ')} && `
}
// Why `env` rather than the bare `NAME=value cmd` prefix: fish rejects that form
// outright ("Unsupported use of '='"), and the pane's POSIX dialect is not
// recorded anywhere Orca can read at write time. `env` parses in sh, bash, zsh
// and fish alike, and matches how every other Orca launch path reaches the agent
// binary — through the executable, not a shell alias.
return `env ${entries.map(([name, value]) => `${name}=${quote(value)}`).join(' ')} `
}
/** Prefixes a typed command so the assignments apply to that invocation. */
export function withInlineEnvAssignments(args: {
command: string
env: Record<string, string>
platform: NodeJS.Platform
shell?: AgentStartupShell
}): string {
return `${buildInlineEnvCommandPrefix(args)}${args.command}`
}