mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 16:02:29 +00:00
* fix(remote): focus host-delegated split panes Return the authoritative leaf identity from terminal.split, record viewer-local focus intent behind the captured pairing revision, and replay the mirrored layout before focusing the exact pane. Preserve old-host fallback and prevent delayed split responses from stealing focus after the viewer moves away. Add deterministic runtime, renderer, concurrency, compatibility, and headed paired-Electron coverage for Cmd+D, header splits, and immediate PTY input routing. Fixes #16510 * fix(remote): preserve split focus across tab groups Resolve the initiating source tab and leaf from the remote PTY, while keeping the viewer's current focus as a separate anti-steal baseline. This lets context-menu/header splits from non-focused group tabs focus their result without allowing delayed responses to override a later navigation. * test(remote): drive split focus with key events * test(remote): use the platform split shortcut * fix(remote): fence concurrent split focus intent * fix(remote): harden split focus ordering * fix(remote): preserve split focus after runtime refactor * fix(remote): fence stale split focus gestures * test(remote): keep split focus regression within line budget
323 lines
8.6 KiB
TypeScript
323 lines
8.6 KiB
TypeScript
import type { AgentSessionPtyWriteRefusal } from './agent-session-pty-write-admission'
|
|
import type {
|
|
AgentProviderSessionMetadata,
|
|
SleepingAgentLaunchConfig
|
|
} from './agent-session-resume'
|
|
import type { StartupCommandDelivery } from './codex-startup-delivery'
|
|
import type { ExecutionHostId } from './execution-host'
|
|
import type { PtyIncarnationId } from './pty-incarnation'
|
|
import type { RuntimeMobileSessionTabsResult } from './runtime-session-contracts'
|
|
import type { TabGroupLayoutNode } from './tab-types'
|
|
import type { TerminalExitCause } from './terminal-exit-cause'
|
|
import type { TerminalPaneLayoutNode } from './terminal-tab-types'
|
|
import type { TuiAgent } from './tui-agent'
|
|
|
|
export type RuntimeTerminalSummary = {
|
|
handle: string
|
|
ptyId: string | null
|
|
incarnationId?: string | null
|
|
orphaned?: boolean
|
|
worktreeId: string
|
|
worktreePath: string
|
|
branch: string
|
|
tabId: string
|
|
leafId: string
|
|
title: string | null
|
|
connected: boolean
|
|
writable: boolean
|
|
lastOutputAt: number | null
|
|
preview: string
|
|
/** Host-resolved agent identity for action consumers; absent when unknown or unsupported. */
|
|
agentIdentity?: TuiAgent
|
|
/** Absent while running or when the host predates the field; never infer a clean finish. */
|
|
exitCause?: TerminalExitCause
|
|
/** Absent when the host predates the field or could not name the execution host. */
|
|
executionHostId?: ExecutionHostId
|
|
}
|
|
|
|
export type RuntimeTerminalVisualTerminalNode = {
|
|
type: 'terminal'
|
|
handle: string
|
|
tabId: string
|
|
leafId: string
|
|
title: string | null
|
|
connected: boolean
|
|
active: boolean
|
|
}
|
|
|
|
export type RuntimeTerminalVisualPaneNode =
|
|
| RuntimeTerminalVisualTerminalNode
|
|
| {
|
|
type: 'pane-split'
|
|
direction: Extract<TerminalPaneLayoutNode, { type: 'split' }>['direction']
|
|
first: RuntimeTerminalVisualPaneNode
|
|
second: RuntimeTerminalVisualPaneNode
|
|
}
|
|
|
|
export type RuntimeTerminalVisualTab = {
|
|
tabId: string
|
|
title: string | null
|
|
activeLeafId: string | null
|
|
panes: RuntimeTerminalVisualPaneNode
|
|
}
|
|
|
|
export type RuntimeTerminalVisualGroupNode = {
|
|
type: 'group'
|
|
groupId: string | null
|
|
activeTabId: string | null
|
|
tabs: RuntimeTerminalVisualTab[]
|
|
}
|
|
|
|
export type RuntimeTerminalVisualLayoutNode =
|
|
| RuntimeTerminalVisualGroupNode
|
|
| {
|
|
type: 'split'
|
|
direction: Extract<TabGroupLayoutNode, { type: 'split' }>['direction']
|
|
first: RuntimeTerminalVisualLayoutNode
|
|
second: RuntimeTerminalVisualLayoutNode
|
|
}
|
|
|
|
export type RuntimeTerminalVisualLayout = {
|
|
worktreeId: string
|
|
worktreePath: string
|
|
root: RuntimeTerminalVisualLayoutNode
|
|
}
|
|
|
|
export type RuntimeTerminalListHostScope = {
|
|
hostIds: ExecutionHostId[]
|
|
omittedHostIds: ExecutionHostId[]
|
|
}
|
|
|
|
export type RuntimeTerminalListResult = {
|
|
terminals: RuntimeTerminalSummary[]
|
|
visualLayouts?: RuntimeTerminalVisualLayout[]
|
|
topologyRevisions?: Record<string, number>
|
|
totalCount: number
|
|
truncated: boolean
|
|
/** Absent from hosts that predate the field; treat that scope as unverifiable. */
|
|
hostScope?: RuntimeTerminalListHostScope
|
|
}
|
|
|
|
export type RuntimeTerminalOrphanAdoptionClaim = {
|
|
terminal: string
|
|
ptyId: string
|
|
incarnationId: PtyIncarnationId
|
|
tabId: string
|
|
leafId: string
|
|
}
|
|
|
|
export type RuntimeTerminalOrphanTopologyTab = {
|
|
tabId: string
|
|
root: TerminalPaneLayoutNode
|
|
activeLeafId: string
|
|
expandedLeafId: string | null
|
|
}
|
|
|
|
export type RuntimeTerminalOrphanTopologyGroup = {
|
|
id: string
|
|
activeTabId: string
|
|
tabOrder: string[]
|
|
recentTabIds?: string[]
|
|
}
|
|
|
|
export type RuntimeTerminalOrphanTopology = {
|
|
tabs: RuntimeTerminalOrphanTopologyTab[]
|
|
groups: RuntimeTerminalOrphanTopologyGroup[]
|
|
groupLayout?: TabGroupLayoutNode
|
|
}
|
|
|
|
export type RuntimeTerminalOrphanAdoptionRequest = {
|
|
worktree: string
|
|
expectedTopologyRevision: number
|
|
claims: RuntimeTerminalOrphanAdoptionClaim[]
|
|
activeTabId?: string
|
|
activeGroupId?: string
|
|
topology?: RuntimeTerminalOrphanTopology
|
|
}
|
|
|
|
export type RuntimeTerminalOrphanAdoptionResult = {
|
|
adopted: boolean
|
|
topologyRevision: number
|
|
snapshot: RuntimeMobileSessionTabsResult
|
|
}
|
|
|
|
export type RuntimeWorktreeTerminalSleepResult = {
|
|
stopped: number
|
|
stoppedPtyIds: string[]
|
|
livePtyIds: string[]
|
|
} & (
|
|
| { postStopVerified: true; postStopFailure?: never; remainingLivePtyIds?: never }
|
|
| {
|
|
postStopVerified: false
|
|
postStopFailure: 'terminal_liveness_unavailable'
|
|
remainingLivePtyIds?: never
|
|
}
|
|
| {
|
|
postStopVerified: false
|
|
postStopFailure: 'terminal_worktree_sleep_still_live'
|
|
remainingLivePtyIds: string[]
|
|
}
|
|
)
|
|
|
|
export type RuntimeTerminalInteractiveWaitSource = 'hook' | 'prompt-text' | 'title'
|
|
|
|
export type RuntimeTerminalInteractiveWait = {
|
|
source: RuntimeTerminalInteractiveWaitSource
|
|
reason?: RuntimeTerminalWaitBlockedReason
|
|
since?: number
|
|
}
|
|
|
|
export type RuntimeTerminalShow = RuntimeTerminalSummary & {
|
|
paneRuntimeId: number
|
|
ptyId: string | null
|
|
rendererGraphEpoch: number
|
|
/** Null means evaluated with no wait; absent means not evaluated. */
|
|
agentWait?: RuntimeTerminalInteractiveWait | null
|
|
}
|
|
|
|
export type RuntimeTerminalState = 'running' | 'exited' | 'unknown'
|
|
|
|
export type RuntimeTerminalRead = {
|
|
handle: string
|
|
status: RuntimeTerminalState
|
|
tail: string[]
|
|
truncated: boolean
|
|
limited?: boolean
|
|
oldestCursor?: string
|
|
nextCursor: string | null
|
|
latestCursor?: string
|
|
returnedLineCount?: number
|
|
source?: 'stream' | 'screen' | 'screen-unavailable'
|
|
/** UI-only composer text, excluded from `tail`. */
|
|
draft?: string
|
|
}
|
|
|
|
export type RuntimeTerminalRename = {
|
|
handle: string
|
|
tabId: string
|
|
title: string | null
|
|
}
|
|
|
|
export type RuntimeTerminalSend = {
|
|
handle: string
|
|
accepted: boolean
|
|
bytesWritten: number
|
|
refusedReason?: 'no-agent' | 'permission'
|
|
/**
|
|
* Present only when a durable agent-session lease refused the write. Additive and optional: an
|
|
* old client sees the `accepted: false` it already handles and ignores this field.
|
|
*/
|
|
agentSessionRefusal?: AgentSessionPtyWriteRefusal
|
|
}
|
|
|
|
export type RuntimeTerminalAgentStatusState = 'working' | 'permission' | 'idle' | null
|
|
|
|
export type RuntimeTerminalAgentStatus = {
|
|
handle: string
|
|
isRunningAgent: boolean
|
|
status: RuntimeTerminalAgentStatusState
|
|
}
|
|
|
|
export type RuntimeTerminalPresentation = 'background' | 'focused'
|
|
|
|
type RuntimeTerminalCreateBaseRequestPayload = {
|
|
requestId: string
|
|
worktreeId?: string
|
|
afterTabId?: string
|
|
targetGroupId?: string
|
|
command?: string
|
|
cwd?: string
|
|
env?: Record<string, string>
|
|
envToDelete?: string[]
|
|
launchConfig?: SleepingAgentLaunchConfig
|
|
resumeProviderSession?: AgentProviderSessionMetadata
|
|
launchToken?: string
|
|
launchAgent?: TuiAgent
|
|
viewMode?: 'terminal' | 'chat'
|
|
startupCommandDelivery?: StartupCommandDelivery
|
|
title?: string
|
|
activate?: boolean
|
|
presentation?: RuntimeTerminalPresentation
|
|
surfaceOwner?: false
|
|
}
|
|
|
|
export type RuntimeTerminalCreateRequestPayload =
|
|
| (RuntimeTerminalCreateBaseRequestPayload & { source?: undefined })
|
|
| (RuntimeTerminalCreateBaseRequestPayload & {
|
|
worktreeId: string
|
|
source: 'runtime-session'
|
|
})
|
|
|
|
export type RuntimeTerminalCreate = {
|
|
handle: string
|
|
tabId?: string
|
|
paneKey?: string | null
|
|
ptyId?: string | null
|
|
worktreeId: string
|
|
title: string | null
|
|
executionHostId?: ExecutionHostId
|
|
hostPlatform?: NodeJS.Platform
|
|
surface?: 'background' | 'visible'
|
|
warning?: string
|
|
agentSessionDisposition?: 'created' | 'adopted'
|
|
isReattach?: true
|
|
/** Spawn process identity for host-internal ownership proof. */
|
|
processId?: number
|
|
}
|
|
|
|
export type RuntimeTerminalSplit = {
|
|
handle: string
|
|
tabId: string
|
|
paneRuntimeId: number
|
|
// Why: paired callers need the host-created leaf identity to focus the exact pane.
|
|
leafId?: string
|
|
}
|
|
|
|
export type RuntimeTerminalResolvePane = {
|
|
handle: string
|
|
tabId: string
|
|
leafId: string
|
|
ptyId: string | null
|
|
connected?: boolean
|
|
worktreeId?: string
|
|
executionHostId?: ExecutionHostId
|
|
hostPlatform?: NodeJS.Platform
|
|
}
|
|
|
|
export type RuntimeTerminalFocus = {
|
|
handle: string
|
|
tabId: string
|
|
worktreeId: string
|
|
navigated?: boolean
|
|
}
|
|
|
|
export type RuntimeTerminalClose = {
|
|
handle: string
|
|
tabId: string
|
|
closeMode?: 'tab'
|
|
ptyKilled: boolean
|
|
ptyStopVerdict?: 'live' | 'unverifiable'
|
|
ptyStopReason?: string
|
|
}
|
|
|
|
export type RuntimeTerminalWaitCondition = 'exit' | 'tui-idle'
|
|
|
|
export type RuntimeTerminalWaitBlockedReason =
|
|
| 'codex-update-prompt'
|
|
| 'codex-trust-workspace'
|
|
| 'codex-cwd-prompt'
|
|
| 'codex-model-migration-prompt'
|
|
| 'codex-hooks-review-prompt'
|
|
| 'codex-interactive-prompt'
|
|
| 'agent-approval-prompt'
|
|
|
|
export type RuntimeTerminalWait = {
|
|
handle: string
|
|
condition: RuntimeTerminalWaitCondition
|
|
satisfied: boolean
|
|
status: RuntimeTerminalState
|
|
exitCode: number | null
|
|
exitCause?: TerminalExitCause
|
|
blockedReason?: RuntimeTerminalWaitBlockedReason
|
|
}
|