mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 08:03:58 +00:00
fix terminal incarnation propagation on recovery
This commit is contained in:
@@ -10471,6 +10471,37 @@ describe('OrcaRuntimeService', () => {
|
||||
expect(batches.flatMap((batch) => batch.facts)).not.toContainEqual({ kind: 'agent-exited' })
|
||||
})
|
||||
|
||||
it('confirms a host-certified SSH exit that races the foreground read', async () => {
|
||||
const { runtime, batches } = createSideEffectRuntime()
|
||||
runtime.registerPty('pty-1', TEST_WORKTREE_ID, 'conn-1')
|
||||
syncSinglePty(runtime)
|
||||
runtime.onPtySpawned('pty-1', 'inc-ssh-exit', { awaitsRegistration: false })
|
||||
runtime.ingestSyntheticTitleFrame('pty-1', '\x1b]0;Codex ready\x07')
|
||||
let resolveForeground!: (process: string) => void
|
||||
const foregroundRead = new Promise<string>((resolve) => {
|
||||
resolveForeground = resolve
|
||||
})
|
||||
const getForegroundProcess = vi.fn(() => foregroundRead)
|
||||
runtime.setPtyController({
|
||||
write: () => true,
|
||||
kill: () => true,
|
||||
getForegroundProcess
|
||||
})
|
||||
|
||||
runtime.onPtyData('pty-1', '\x1b]0;bichir\x07', 100)
|
||||
await vi.waitFor(() => expect(getForegroundProcess).toHaveBeenCalledOnce())
|
||||
runtime.onPtyExit('pty-1', -1, 'inc-ssh-exit', { hostExitConfirmed: true })
|
||||
resolveForeground('zsh')
|
||||
|
||||
await vi.waitFor(() =>
|
||||
expect(batches.flatMap((batch) => batch.facts)).toContainEqual({
|
||||
kind: 'agent-exited',
|
||||
executionHostConfirmed: true,
|
||||
incarnationId: 'inc-ssh-exit'
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('still confirms a local agent exit if the PTY disconnects during the foreground read', async () => {
|
||||
const { runtime, batches } = createSideEffectRuntime()
|
||||
runtime.registerPty('pty-1', TEST_WORKTREE_ID)
|
||||
@@ -15549,7 +15580,7 @@ describe('OrcaRuntimeService', () => {
|
||||
})
|
||||
|
||||
it('adopts renderer pane identity for remote runtime terminal creates', async () => {
|
||||
const spawn = vi.fn().mockResolvedValue({ id: 'pty-bg' })
|
||||
const spawn = vi.fn().mockResolvedValue({ id: 'pty-bg', incarnationId: 'inc-remote-create' })
|
||||
const runtime = new OrcaRuntimeService(store)
|
||||
const tabId = 'tab-remote-runtime'
|
||||
const leafId = '11111111-1111-4111-8111-111111111111'
|
||||
@@ -15560,7 +15591,7 @@ describe('OrcaRuntimeService', () => {
|
||||
getForegroundProcess: async () => null
|
||||
})
|
||||
|
||||
await runtime.createTerminal(`path:${TEST_WORKTREE_PATH}`, {
|
||||
const terminal = await runtime.createTerminal(`path:${TEST_WORKTREE_PATH}`, {
|
||||
focus: false,
|
||||
tabId,
|
||||
leafId,
|
||||
@@ -15574,6 +15605,7 @@ describe('OrcaRuntimeService', () => {
|
||||
(spawn.mock.calls[0]?.[0] as { env?: Record<string, string> } | undefined)?.env ?? {}
|
||||
expect(spawnedEnv.ORCA_TAB_ID).toBe(tabId)
|
||||
expect(spawnedEnv.ORCA_PANE_KEY).toBe(`${tabId}:${leafId}`)
|
||||
expect(terminal.incarnationId).toBe('inc-remote-create')
|
||||
})
|
||||
|
||||
it('does not adopt web mirror ids as host terminal ids', async () => {
|
||||
|
||||
@@ -1560,6 +1560,8 @@ type RuntimePtyWorktreeRecord = {
|
||||
launchAgent: TuiAgent | null
|
||||
foregroundAgent: TuiAgent | null
|
||||
connected: boolean
|
||||
/** Host-certified exit survives connected=false until pending title reads settle. */
|
||||
hostExitConfirmed: boolean
|
||||
disconnectedAt: number | null
|
||||
lastExitCode: number | null
|
||||
lastExitCause: TerminalExitCause | null
|
||||
@@ -11259,6 +11261,7 @@ export class OrcaRuntimeService {
|
||||
this.spawnPublishedPtys.add(ptyId)
|
||||
const pty = this.getOrCreatePtyWorktreeRecord(ptyId)
|
||||
if (pty) {
|
||||
pty.hostExitConfirmed = false
|
||||
if (incarnationId) {
|
||||
pty.incarnationId = incarnationId
|
||||
}
|
||||
@@ -11303,6 +11306,7 @@ export class OrcaRuntimeService {
|
||||
...(binding && paneKey ? { tabId: binding.tabId, paneKey } : {}),
|
||||
...(binding?.incarnationId ? { incarnationId: binding.incarnationId } : {})
|
||||
})
|
||||
pty.hostExitConfirmed = false
|
||||
const agentLaunchAuthority = binding?.agentLaunchAuthority
|
||||
if (
|
||||
agentLaunchAuthority &&
|
||||
@@ -15917,6 +15921,9 @@ export class OrcaRuntimeService {
|
||||
this.remoteDesktopViewerRevisions.delete(ptyId)
|
||||
this.disposeHeadlessTerminal(ptyId)
|
||||
if (pty) {
|
||||
if (options?.hostExitConfirmed === true) {
|
||||
pty.hostExitConfirmed = true
|
||||
}
|
||||
pty.connected = false
|
||||
pty.runtimeSessionOwned = false
|
||||
this.setPairedRendererSessionOwnership(pty.ptyId, false)
|
||||
@@ -18953,6 +18960,7 @@ export class OrcaRuntimeService {
|
||||
tabId: parsed?.tabId ?? record?.tabId ?? '',
|
||||
leafId: parsed?.leafId ?? record?.leafId ?? '',
|
||||
ptyId: record?.ptyId ?? null,
|
||||
...(pty?.incarnationId ? { incarnationId: pty.incarnationId } : {}),
|
||||
connected: pty?.connected === true,
|
||||
...(worktreeId ? { worktreeId } : {}),
|
||||
...this.getPtyExecutionHostMetadata(record?.ptyId ?? pty?.ptyId ?? null)
|
||||
@@ -19003,6 +19011,7 @@ export class OrcaRuntimeService {
|
||||
tabId: parsed.tabId,
|
||||
leafId: parsed.leafId,
|
||||
ptyId: terminal.ptyId ?? null,
|
||||
...(terminal.incarnationId ? { incarnationId: terminal.incarnationId } : {}),
|
||||
worktreeId: expectedWorktreeId
|
||||
}))
|
||||
this.terminalPaneRecoveryByIdentity.set(recoveryKey, recovery)
|
||||
@@ -19667,7 +19676,7 @@ export class OrcaRuntimeService {
|
||||
if (this.getPtyLivenessVerdict(ptyId)?.status === 'unverifiable') {
|
||||
return true
|
||||
}
|
||||
return Boolean(pty?.connectionId && !pty.connected)
|
||||
return Boolean(pty?.connectionId && !pty.connected && !pty.hostExitConfirmed)
|
||||
}
|
||||
|
||||
private confirmPtyAgentExit(ptyId: string): void {
|
||||
@@ -29131,6 +29140,7 @@ export class OrcaRuntimeService {
|
||||
tabId,
|
||||
paneKey,
|
||||
ptyId: result.id,
|
||||
...(result.incarnationId ? { incarnationId: result.incarnationId } : {}),
|
||||
worktreeId: workspace.id,
|
||||
title: pty?.title ?? launchOpts.title ?? null,
|
||||
...this.getPtyExecutionHostMetadata(result.id),
|
||||
@@ -29212,12 +29222,15 @@ export class OrcaRuntimeService {
|
||||
// populates this.leaves may not have arrived yet. Wait for the leaf to
|
||||
// appear so we can return a valid handle the caller can use right away.
|
||||
const handle = await this.waitForTerminalHandle(reply.tabId)
|
||||
const ptyId = this.handles.get(handle)?.ptyId ?? null
|
||||
const incarnationId = ptyId ? (this.ptysById.get(ptyId)?.incarnationId ?? null) : null
|
||||
return {
|
||||
handle,
|
||||
tabId: reply.tabId,
|
||||
worktreeId: worktreeId ?? '',
|
||||
title: reply.title,
|
||||
...this.getPtyExecutionHostMetadata(this.handles.get(handle)?.ptyId ?? null),
|
||||
...(incarnationId ? { incarnationId } : {}),
|
||||
...this.getPtyExecutionHostMetadata(ptyId),
|
||||
surface: 'visible'
|
||||
}
|
||||
}
|
||||
@@ -29301,7 +29314,8 @@ export class OrcaRuntimeService {
|
||||
this.adoptControllerTerminalHandle(session.id, terminalHandle)
|
||||
const pty = this.recordPtyWorktree(session.id, worktreeId, {
|
||||
connected: true,
|
||||
title: session.title
|
||||
title: session.title,
|
||||
...(session.incarnationId ? { incarnationId: session.incarnationId } : {})
|
||||
})
|
||||
const adoptedHandle = this.issuePtyHandle(pty)
|
||||
if (adoptedHandle !== terminalHandle) {
|
||||
@@ -29310,6 +29324,7 @@ export class OrcaRuntimeService {
|
||||
return {
|
||||
handle: adoptedHandle,
|
||||
ptyId: session.id,
|
||||
...(session.incarnationId ? { incarnationId: session.incarnationId } : {}),
|
||||
worktreeId,
|
||||
title: session.title || null,
|
||||
surface: 'background'
|
||||
@@ -33182,6 +33197,7 @@ export class OrcaRuntimeService {
|
||||
launchAgent: null,
|
||||
foregroundAgent: null,
|
||||
connected: state.connected ?? true,
|
||||
hostExitConfirmed: false,
|
||||
disconnectedAt: state.connected === false ? Date.now() : null,
|
||||
lastExitCode: null,
|
||||
lastExitCause: null,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { IDisposable } from '@xterm/xterm'
|
||||
import type { HasPty } from '../terminal-dead-session-reconcile'
|
||||
import type { PtyIncarnationId } from '../../../../../shared/pty-incarnation'
|
||||
|
||||
export type PanePtyBinding = IDisposable & {
|
||||
syncProcessTracking: () => void
|
||||
@@ -27,4 +28,6 @@ export type PanePtyBinding = IDisposable & {
|
||||
* preserves on exit. Read at unmount so the parked sidecar can carry the
|
||||
* fact as a plain value instead of a session reference. */
|
||||
isUntouchedFreshSpawnPty: (ptyId: string) => boolean
|
||||
/** Captured by parked watchers so confirmed exit facts retain incarnation authority. */
|
||||
getPtyIncarnationId?: (ptyId: string) => PtyIncarnationId | null
|
||||
}
|
||||
|
||||
+6
-1
@@ -130,12 +130,17 @@ export function installPanePtyVisibilityBind(session: ConnectPanePtySession): vo
|
||||
// Do not strand a successful spawn because a delivery callback failed.
|
||||
}
|
||||
}
|
||||
session.onPtyRebind = (ptyId: string, replacedPtyId: string): void => {
|
||||
session.onPtyRebind = (
|
||||
ptyId: string,
|
||||
replacedPtyId: string,
|
||||
incarnationId?: PtyIncarnationId
|
||||
): void => {
|
||||
if (!session.canAdoptCapturedDirectSshRetryPty(ptyId)) {
|
||||
return
|
||||
}
|
||||
// Why: provider handle rotation keeps the existing pane/session generation;
|
||||
// replace its stale store identity without fresh-spawn exit semantics.
|
||||
session.ptyIncarnationId = incarnationId ?? null
|
||||
session.bindActivePanePty(ptyId, { replacePtyId: replacedPtyId })
|
||||
}
|
||||
// ─── Attention signal: BEL ────────────────────────────────────────────
|
||||
|
||||
@@ -174,6 +174,8 @@ export function installSessionReconcileDispose(session: ConnectPanePtySession):
|
||||
reconcileIfSessionMissing: session.reconcileIfSessionMissing,
|
||||
isUntouchedFreshSpawnPty: (ptyId) =>
|
||||
session.spawnedFreshPtyId === ptyId && !Number.isFinite(session.lastTerminalInputAt),
|
||||
getPtyIncarnationId: (ptyId) =>
|
||||
session.transport.getPtyId() === ptyId ? session.ptyIncarnationId : null,
|
||||
dispose() {
|
||||
session.disposed = true
|
||||
// Why: a detached client stops observing the pane's bytes, so it must cede
|
||||
|
||||
@@ -258,7 +258,7 @@ export type IpcPtyTransportOptions = {
|
||||
onTitleChange?: (title: string, rawTitle: string) => void
|
||||
onPtySpawn?: (ptyId: string, incarnationId?: PtyIncarnationId) => void
|
||||
/** Rebind an existing pane after its provider replaces the PTY identity. */
|
||||
onPtyRebind?: (ptyId: string, replacedPtyId: string) => void
|
||||
onPtyRebind?: (ptyId: string, replacedPtyId: string, incarnationId?: PtyIncarnationId) => void
|
||||
onBell?: () => void
|
||||
onAgentBecameIdle?: (title: string) => void
|
||||
onAgentBecameWorking?: () => void
|
||||
|
||||
+3
-1
@@ -93,6 +93,7 @@ describe('createRemoteRuntimePtyTransport', () => {
|
||||
tabId: 'host-tab-1',
|
||||
leafId: 'pane:1',
|
||||
ptyId: 'ssh-private-pty',
|
||||
incarnationId: 'replacement-incarnation',
|
||||
worktreeId: 'wt-1'
|
||||
}
|
||||
}
|
||||
@@ -126,7 +127,8 @@ describe('createRemoteRuntimePtyTransport', () => {
|
||||
expect(latestSubscribePayload()).toMatchObject({ terminal: 'terminal-replacement' })
|
||||
expect(onPtyRebind).toHaveBeenCalledWith(
|
||||
'remote:hub-env@@terminal-replacement',
|
||||
'remote:hub-env@@terminal-expired'
|
||||
'remote:hub-env@@terminal-expired',
|
||||
'replacement-incarnation'
|
||||
)
|
||||
expect(onPtyExit).not.toHaveBeenCalled()
|
||||
expect(onError).not.toHaveBeenCalled()
|
||||
|
||||
+6
-1
@@ -47,6 +47,7 @@ describe('createRemoteRuntimePtyTransport', () => {
|
||||
tabId: 'tab-1',
|
||||
leafId,
|
||||
ptyId: 'ssh:hub-private@@pty-2',
|
||||
incarnationId: 'hub-incarnation-1',
|
||||
worktreeId: 'wt-1',
|
||||
executionHostId: 'ssh:hub-private',
|
||||
hostPlatform: 'win32'
|
||||
@@ -57,10 +58,12 @@ describe('createRemoteRuntimePtyTransport', () => {
|
||||
throw new Error(`Unexpected method ${request.method}`)
|
||||
})
|
||||
const { createRemoteRuntimePtyTransport } = await import('./remote-runtime-pty-transport')
|
||||
const onPtySpawn = vi.fn()
|
||||
const transport = createRemoteRuntimePtyTransport('hub-env', {
|
||||
worktreeId: 'wt-1',
|
||||
tabId: 'tab-1',
|
||||
leafId
|
||||
leafId,
|
||||
onPtySpawn
|
||||
})
|
||||
|
||||
const result = await transport.connect({
|
||||
@@ -73,9 +76,11 @@ describe('createRemoteRuntimePtyTransport', () => {
|
||||
|
||||
expect(result).toEqual({
|
||||
id: 'remote:hub-env@@hub-terminal-1',
|
||||
incarnationId: 'hub-incarnation-1',
|
||||
replay: '',
|
||||
isReattach: true
|
||||
})
|
||||
expect(onPtySpawn).toHaveBeenCalledWith('remote:hub-env@@hub-terminal-1', 'hub-incarnation-1')
|
||||
expect(transport.getPtyId()).toBe('remote:hub-env@@hub-terminal-1')
|
||||
expect(transport.getExecutionHostId?.()).toBe('ssh:hub-private')
|
||||
expect(transport.getRemotePlatform?.()).toBe('win32')
|
||||
|
||||
@@ -202,6 +202,29 @@ export function createRemoteRuntimePtyTransport(
|
||||
let attachGeneration = 0
|
||||
let subscriptionGeneration = 0
|
||||
|
||||
function notifyPtySpawnForRemote(
|
||||
id: string,
|
||||
incarnationId?: RuntimeTerminalCreate['incarnationId']
|
||||
): void {
|
||||
if (incarnationId) {
|
||||
onPtySpawn?.(id, incarnationId)
|
||||
} else {
|
||||
onPtySpawn?.(id)
|
||||
}
|
||||
}
|
||||
|
||||
function notifyPtyRebindForRemote(
|
||||
id: string,
|
||||
replacedId: string,
|
||||
incarnationId?: RuntimeTerminalResolvePane['incarnationId']
|
||||
): void {
|
||||
if (incarnationId) {
|
||||
onPtyRebind?.(id, replacedId, incarnationId)
|
||||
} else {
|
||||
onPtyRebind?.(id, replacedId)
|
||||
}
|
||||
}
|
||||
|
||||
function setAttachmentReady(ready: boolean): void {
|
||||
attachmentReady = ready
|
||||
if (!ready) {
|
||||
@@ -832,6 +855,7 @@ export function createRemoteRuntimePtyTransport(
|
||||
return undefined
|
||||
}
|
||||
|
||||
let resolvedIncarnationId: RuntimeTerminalResolvePane['incarnationId']
|
||||
if (leafId && worktreeId && !resolvePaneUnavailable) {
|
||||
try {
|
||||
const resolved = await callRuntime<{ terminal: RuntimeTerminalResolvePane }>(
|
||||
@@ -846,6 +870,7 @@ export function createRemoteRuntimePtyTransport(
|
||||
(!terminal.worktreeId || terminal.worktreeId === worktreeId)
|
||||
) {
|
||||
adoptExecutionMetadata(terminal)
|
||||
resolvedIncarnationId = terminal.incarnationId
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof RuntimeRpcCallError && error.code === 'method_not_found') {
|
||||
@@ -866,7 +891,7 @@ export function createRemoteRuntimePtyTransport(
|
||||
rows: options.rows ?? 24
|
||||
}
|
||||
if (notifySpawn) {
|
||||
onPtySpawn?.(remotePtyId)
|
||||
notifyPtySpawnForRemote(remotePtyId, resolvedIncarnationId)
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -882,6 +907,7 @@ export function createRemoteRuntimePtyTransport(
|
||||
|
||||
return {
|
||||
id: remotePtyId,
|
||||
...(resolvedIncarnationId ? { incarnationId: resolvedIncarnationId } : {}),
|
||||
replay: '',
|
||||
isReattach: true
|
||||
} satisfies PtyConnectResult
|
||||
@@ -1149,7 +1175,7 @@ export function createRemoteRuntimePtyTransport(
|
||||
rows: options.rows ?? 24
|
||||
}
|
||||
if (notifySpawn) {
|
||||
onPtySpawn?.(remotePtyId)
|
||||
notifyPtySpawnForRemote(remotePtyId, terminal.incarnationId)
|
||||
}
|
||||
emitRecoveryState()
|
||||
try {
|
||||
@@ -1167,7 +1193,12 @@ export function createRemoteRuntimePtyTransport(
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
return { id: remotePtyId, replay: '', isReattach: true }
|
||||
return {
|
||||
id: remotePtyId,
|
||||
...(terminal.incarnationId ? { incarnationId: terminal.incarnationId } : {}),
|
||||
replay: '',
|
||||
isReattach: true
|
||||
}
|
||||
}
|
||||
|
||||
function recoverExpiredHostPane(): void {
|
||||
@@ -1199,7 +1230,7 @@ export function createRemoteRuntimePtyTransport(
|
||||
if (replacedPtyId && replacedPtyId !== remotePtyId) {
|
||||
replaceFitOverridePtyId(replacedPtyId, remotePtyId)
|
||||
replaceDriverPtyId(replacedPtyId, remotePtyId)
|
||||
onPtyRebind?.(remotePtyId, replacedPtyId)
|
||||
notifyPtyRebindForRemote(remotePtyId, replacedPtyId, terminal.incarnationId)
|
||||
}
|
||||
await subscribeToHandle()
|
||||
})
|
||||
@@ -2179,7 +2210,7 @@ export function createRemoteRuntimePtyTransport(
|
||||
rows: options.rows ?? 24
|
||||
}
|
||||
if (createdTerminal.isReattach !== true) {
|
||||
onPtySpawn?.(remotePtyId)
|
||||
notifyPtySpawnForRemote(remotePtyId, createdTerminal.incarnationId)
|
||||
}
|
||||
emitRecoveryState()
|
||||
|
||||
@@ -2196,6 +2227,9 @@ export function createRemoteRuntimePtyTransport(
|
||||
|
||||
return {
|
||||
id: remotePtyId,
|
||||
...(createdTerminal.incarnationId
|
||||
? { incarnationId: createdTerminal.incarnationId }
|
||||
: {}),
|
||||
replay: '',
|
||||
...(createdTerminal.isReattach === true ? { isReattach: true } : {})
|
||||
} satisfies PtyConnectResult
|
||||
|
||||
@@ -119,6 +119,7 @@ export function startParkedPtyWatcher(args: {
|
||||
const initialTitle = state.runtimePaneTitlesByTabId[tab.id]?.[pane.paneId]
|
||||
const disposeWatcher = startParkedTerminalByteWatcher({
|
||||
ptyId,
|
||||
...(pane.incarnationId ? { incarnationId: pane.incarnationId } : {}),
|
||||
tabId: tab.id,
|
||||
worktreeId,
|
||||
leafId: pane.leafId,
|
||||
|
||||
@@ -70,6 +70,9 @@ export function resolveParkedTerminalPaneCandidates(
|
||||
// Why: the fact belongs to the captured PTY; a re-minted id on this leaf is a different shell.
|
||||
...(prior.ptyId === pane.ptyId && prior.untouchedFreshSpawn
|
||||
? { untouchedFreshSpawn: true }
|
||||
: {}),
|
||||
...(prior.ptyId === pane.ptyId && prior.incarnationId
|
||||
? { incarnationId: prior.incarnationId }
|
||||
: {})
|
||||
}
|
||||
: pane
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
import { discardPreHandlerPtyState, hasPreHandlerPtyExit } from './pty-pre-handler-buffer'
|
||||
import { parseRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants'
|
||||
import type { PtyIncarnationId } from '../../../../shared/pty-incarnation'
|
||||
|
||||
export type ParkedTerminalPaneCapture = {
|
||||
ptyId: string | null
|
||||
/** Process identity carried across the mounted-to-parked handoff. */
|
||||
incarnationId?: PtyIncarnationId | null
|
||||
/** PaneManager numeric pane id the live pane used for runtime titles. */
|
||||
paneId: number
|
||||
/** Stable terminal-layout leaf UUID (paneKey attribution). */
|
||||
|
||||
@@ -1963,10 +1963,17 @@ export function useTerminalPaneLifecycle({
|
||||
manager.getPanes().map((capturedPane) => {
|
||||
const capturedPtyId = paneTransports.get(capturedPane.id)?.getPtyId() ?? null
|
||||
const capturedBinding = panePtyBindings.get(capturedPane.id) as
|
||||
| (IDisposable & { isUntouchedFreshSpawnPty?: (ptyId: string) => boolean })
|
||||
| (IDisposable & {
|
||||
isUntouchedFreshSpawnPty?: (ptyId: string) => boolean
|
||||
getPtyIncarnationId?: (ptyId: string) => string | null
|
||||
})
|
||||
| undefined
|
||||
return {
|
||||
ptyId: capturedPtyId,
|
||||
incarnationId:
|
||||
capturedPtyId !== null
|
||||
? (capturedBinding?.getPtyIncarnationId?.(capturedPtyId) ?? null)
|
||||
: null,
|
||||
paneId: capturedPane.id,
|
||||
leafId: capturedPane.leafId,
|
||||
drivesTabTitle: manager.getActivePane()?.id === capturedPane.id,
|
||||
|
||||
@@ -243,6 +243,8 @@ export type RuntimeTerminalCreate = {
|
||||
tabId?: string
|
||||
paneKey?: string | null
|
||||
ptyId?: string | null
|
||||
/** Opaque process identity behind a reusable PTY id; absence is unknown. */
|
||||
incarnationId?: PtyIncarnationId
|
||||
worktreeId: string
|
||||
title: string | null
|
||||
executionHostId?: ExecutionHostId
|
||||
@@ -264,6 +266,8 @@ export type RuntimeTerminalResolvePane = {
|
||||
tabId: string
|
||||
leafId: string
|
||||
ptyId: string | null
|
||||
/** Opaque process identity behind a reusable PTY id; absence is unknown. */
|
||||
incarnationId?: PtyIncarnationId
|
||||
connected?: boolean
|
||||
worktreeId?: string
|
||||
executionHostId?: ExecutionHostId
|
||||
|
||||
Reference in New Issue
Block a user