Files
orca/src/main/providers/pty-spawn-result.ts
T
Neil 9927edd631 fix(ssh): let the host say whether it armed the ready marker (#18802)
#18796 made every SSH Codex background launch wait for the shell-ready marker,
but the client cannot see the remote shell. On a host that never publishes one --
fish, sh, Windows, or a relay predating #18796 -- no marker arrives and delivery
falls back at 1.5s where it used to write at 50ms.

The relay already computes whether it armed the marker; publish that as an
optional `shellReadyArmed` on the spawn reply and let the client skip a wait it
now knows is pointless. Absent stays UNKNOWN and keeps the client's own guess, so
an older host behaves exactly as before; false is only ever an answer a host gave.
It rides every reply, false included, or absent would stop meaning "old host".

A host that did not arm the marker did not arm bracketed paste either, so the
released path still submits raw.
2026-09-05 01:34:38 -07:00

97 lines
5.2 KiB
TypeScript

import type { TerminalOscLinkRange } from '../../shared/terminal-osc-link-ranges'
import type { TuiAgent } from '../../shared/tui-agent'
import type { AgentSessionClaimedSpawnResult } from '../../shared/agent-session-host-authority'
import type { PtyIncarnationId } from '../../shared/pty-incarnation'
import type { PtySourceReceivingActivation } from '../../shared/pty-source-receiving-activation'
import type { TerminalOwner } from '../../shared/terminal-owner'
export type PtySpawnResult = {
agentSessionEnsure?: AgentSessionClaimedSpawnResult
/** App-facing PTY id. Remote providers must return globally routable ids,
* not relay-local handles, because renderer/runtime IPC routes by this key. */
id: string
/** Opaque provider-owned identity for this process behind a reusable PTY id. */
incarnationId?: PtyIncarnationId
/** Relay source identity installed before adjacent source frames are decoded. */
sourceActivation?: PtySourceReceivingActivation
/** The provider observed this exact spawn exit before returning its spawn result. */
exitedBeforeSpawnReply?: true
/** Whether the execution host armed the shell-ready marker for a renderer-delivered startup
* command. `false` means the host looked and did not (fish, sh, Windows) so the client must
* not wait; absent means the host predates the field and the client keeps its own guess.
* Never collapse absent into `false`. */
shellReadyArmed?: boolean
/** OS-level pid of the shell process, when available at spawn time.
* Why: the memory collector needs this to walk each PTY's process
* subtree. Daemon-backed providers return it from the RPC result;
* local providers read it from node-pty. Null when the underlying
* provider could not publish a pid (e.g., race during spawn). */
pid?: number | null
/** Minimal allowlisted launch ownership returned by daemon reattach. */
launchAgent?: TuiAgent
/** Local WSL context: null is native; undefined is unavailable/legacy. */
wslDistro?: string | null
/** ANSI snapshot of the terminal screen, present when reattaching to an
* existing daemon session. Write this to xterm.js to restore visual state. */
snapshot?: string
/** Dimensions the snapshot was captured at. Resize xterm.js to these before
* writing the snapshot so ANSI cursor positions land correctly. */
snapshotCols?: number
snapshotRows?: number
/** Normal-buffer history and mode preamble before an alternate-screen frame. */
snapshotPrefixAnsi?: string
/** Visual alternate-screen frame, separate so newer clients can omit it safely. */
snapshotFrameAnsi?: string
/** Live state to append when omitting `snapshotFrameAnsi`. */
snapshotFrameRestoreAnsi?: string
/** Provider sequence at the attach boundary. `reset` starts a new provider
* generation; `continued` resumes the existing absolute domain. */
providerSequence?: {
value: number
generation: 'continued' | 'reset'
}
/** Kitty keyboard flags persisted in the daemon snapshot, threaded so the
* re-seeded runtime emulator answers hidden `CSI ? u` with the real flags
* (terminal-query-authority.md §kitty). Never replayed into a renderer
* xterm — POST_REPLAY_REATTACH_RESET's kitty reset stays authoritative. */
snapshotKittyKeyboardFlags?: number
/** Renderer-domain sequence main reconciled for the attach boundary those
* flags describe. Set by main, not the provider. */
snapshotSeq?: number
/** Ordered ownership evidence proven by the provider snapshot. */
snapshotTerminalOwner?: TerminalOwner
/** True when the spawn reattached to an existing daemon session. */
isReattach?: boolean
/** Grid the PTY is proven to be at once this spawn settled. Only providers whose attach
* applies the requested size set it; daemon/relay attach leave the live grid alone, so main
* must not read the requested dims back as a measurement (see `resolveCommittedPtySize`). */
attachedGrid?: { cols: number; rows: number }
/** Last OSC title tracked by the daemon session the snapshot came from.
* Seeds main's terminal title records after a relaunch; never replayed
* into a terminal. */
lastTitle?: string
/** True when the reattached session uses the alternate screen buffer
* (e.g., Codex CLI, vim). Normal-screen TUIs like Claude Code are false. */
isAlternateScreen?: boolean
/** Buffered output returned by relay pty.attach. Unlike snapshot, this is
* incremental scrollback and must not clear the terminal before replay. */
replay?: string
/** True when the caller requested reattach (sessionId was provided) but the
* relay PTY was gone (grace window elapsed). The renderer uses this to show
* a brief "Session expired — new shell started" message. */
sessionExpired?: boolean
/** Present when cold-restoring from disk history after a daemon crash.
* Contains the saved scrollback and CWD. The new shell spawns in the
* saved CWD; the scrollback is written to xterm.js as read-only history. */
coldRestore?: {
scrollback: string
cwd: string
/** Optional for compatibility with restore payloads from older app code. */
cols?: number
rows?: number
oscLinks?: TerminalOscLinkRange[]
/** Last OSC title from the recovered checkpoint (see `lastTitle` above). */
lastTitle?: string
}
}