Files
orca/src/main/ipc/pty/runtime/controller.ts
T
Brennan Benson 91efcc4df0 Add a per-host running terminal limit setting (#31396)
* feat: configure the running terminal limit for each host

* fix: bound terminal policy saves through fresh confirmation

* Fix truncated zero-size system file reads

* Load actual filesystem fixtures once per test module

* Preserve slept terminal ownership through capacity retries

* Fix terminal limit CI quality checks

* Correct slept terminal resume test types

* Move the slept terminal Resume fix to its own pull request

The Resume correction fixes a bug that already exists at the default limit,
so it now lives in #35401 and this change only adds the terminal limit
setting.

* Keep terminal limits effective when saved settings cannot be read

* Require a numeric edit before repairing terminal limits

* Derive terminal default from startup host capacity

* Apply terminal policy reads when validated results settle

* Align terminal limit tests with static analysis gates

* Inject daemon startup errors at the publication boundary

* Format terminal capacity using the app language
2026-10-29 12:18:02 -07:00

106 lines
5.5 KiB
TypeScript

import { adoptMaterializedRuntimePtySpawn } from './spawn-early'
import { createRuntimePtySpawnState } from './spawn-state'
import { localProvider } from '../provider/registry'
import {
getLocalAutomaticWorkStatus,
localTerminalLimit,
resumeLocalAutomaticWork
} from '../../../daemon/daemon-automatic-work-resume'
import { makePaneKey } from '../../../../shared/stable-pane-id'
import { claimRuntimePaneCreate, makePaneSpawnReservationKey } from '../pane/spawn-reservation'
import type { PtyRuntimeControllerDeps } from './controller-deps'
import { spawnPtyFromRuntimeController } from './spawn'
import {
killPtyFromRuntimeController,
retireRejectedPtyFromRuntimeController,
stopAndWaitPtyFromRuntimeController
} from './kill'
import {
attachPtyFromRuntimeController,
clearBufferFromRuntimeController,
resetInputModesFromRuntimeController,
confirmForegroundProcessFromRuntimeController,
confirmShellForegroundFromRuntimeController,
getCwdFromRuntimeController,
getForegroundProcessFromRuntimeController,
getRendererSerializerGenerationFromRuntimeController,
getSizeFromRuntimeController,
hasChildProcessesFromRuntimeController,
hasPtyFromRuntimeController,
hasRendererSerializerFromRuntimeController,
inspectProcessFromRuntimeController,
probePtyLivenessFromRuntimeController,
resizePtyFromRuntimeController,
serializeProviderBufferFromRuntimeController,
waitForRendererSerializerFromRuntimeController,
writePtyFromRuntimeController
} from './operations'
import { supportsForegroundProcessEvidenceFromRuntimeController } from './foreground-process-evidence-capability'
import {
listProcessesFromRuntimeController,
listProcessesWithHostScopeFromRuntimeController
} from './inventory-operations'
export function installPtyRuntimeController(deps: PtyRuntimeControllerDeps): void {
const { runtime, adoptStablePane, requestSerializedBuffer } = deps
runtime?.setPtyController({
claimStablePaneCreate: (args) => {
const paneKey = makePaneKey(args.tabId, args.leafId)
const ownerKey = makePaneSpawnReservationKey(args.worktreeId, args.connectionId, paneKey)
return ownerKey ? claimRuntimePaneCreate(ownerKey) : () => {}
},
adoptStablePane: async (args) => {
const adopted = await adoptStablePane(args)
if (adopted) {
await adoptMaterializedRuntimePtySpawn(
createRuntimePtySpawnState(deps, { ...args, adoptedStablePane: adopted })
)
}
return adopted
},
spawn: async (args) => spawnPtyFromRuntimeController(deps, args),
// Local owner only: an SSH host's gate belongs to that host's own runtime.
terminalLimit: (update) => localTerminalLimit(localProvider, update),
getAutomaticWorkStatus: () => getLocalAutomaticWorkStatus(localProvider),
resumeAutomaticWork: () => resumeLocalAutomaticWork(localProvider),
write: (ptyId, data, inputKind) => writePtyFromRuntimeController(deps, ptyId, data, inputKind),
writeWithSettlement: (ptyId, data, inputKind) =>
writePtyFromRuntimeController(deps, ptyId, data, inputKind, { waitForSettlement: true }),
probePtyLiveness: (ptyId) => probePtyLivenessFromRuntimeController(deps, ptyId),
// Attach-only ingestion serves unmounted daemon sessions without creating/resizing; SSH uses leases and local PTYs already stream.
attach: (ptyId) => attachPtyFromRuntimeController(deps, ptyId),
kill: (ptyId) => killPtyFromRuntimeController(deps, ptyId),
retireRejectedPty: (ptyId, stopConfirmed, incarnationId) =>
retireRejectedPtyFromRuntimeController(deps, ptyId, stopConfirmed, incarnationId),
stopAndWait: (ptyId, opts) => stopAndWaitPtyFromRuntimeController(deps, ptyId, opts),
getForegroundProcess: (ptyId) => getForegroundProcessFromRuntimeController(ptyId),
inspectProcess: (ptyId, options) => inspectProcessFromRuntimeController(ptyId, options),
confirmForegroundProcess: (ptyId) => confirmForegroundProcessFromRuntimeController(ptyId),
confirmShellForeground: (ptyId) => confirmShellForegroundFromRuntimeController(ptyId),
getCwd: (ptyId) => getCwdFromRuntimeController(ptyId),
hasChildProcesses: (ptyId) => hasChildProcessesFromRuntimeController(ptyId),
clearBuffer: (ptyId) => clearBufferFromRuntimeController(deps, ptyId),
resetInputModes: (ptyId) => resetInputModesFromRuntimeController(deps, ptyId),
hasPty: (ptyId) => hasPtyFromRuntimeController(deps, ptyId),
listProcesses: (hostId, opts) => listProcessesFromRuntimeController(deps, hostId, opts),
listProcessesWithHostScope: (opts) =>
listProcessesWithHostScopeFromRuntimeController(deps, opts),
supportsForegroundProcessEvidence: (hostId) =>
supportsForegroundProcessEvidenceFromRuntimeController(hostId),
serializeBuffer: (ptyId, opts) => {
// Mobile needs exact screen state and geometry before rendering live TUI output.
return requestSerializedBuffer(ptyId, opts)
},
serializeProviderBuffer: (ptyId, opts) =>
serializeProviderBufferFromRuntimeController(ptyId, opts),
hasRendererSerializer: (ptyId) => hasRendererSerializerFromRuntimeController(ptyId),
getRendererSerializerGeneration: (ptyId) =>
getRendererSerializerGenerationFromRuntimeController(ptyId),
waitForRendererSerializer: (ptyId, afterGeneration, timeoutMs, signal) =>
waitForRendererSerializerFromRuntimeController(ptyId, afterGeneration, timeoutMs, signal),
getSize: (ptyId) => getSizeFromRuntimeController(ptyId),
resize: (ptyId, cols, rows) => resizePtyFromRuntimeController(ptyId, cols, rows)
})
}