mirror of
https://github.com/stablyai/orca.git
synced 2026-10-25 08:04:05 +00:00
fix(remote): raise paired terminal stream capacity
This commit is contained in:
@@ -2991,13 +2991,13 @@
|
||||
"providers": ["paired-runtime", "ssh"],
|
||||
"coveredPlatforms": ["macos"],
|
||||
"coveredProviders": ["paired-runtime"],
|
||||
"coverageNotes": "Deterministic headed macOS runs launch an isolated Orca desktop server and a separate paired web client. A byte-identical headless run uses an isolated `orca serve` host. Both create six real paired host PTYs with bounded high-output scrollback, ordinary-park five client xterms without enabling the lossy retention budget, assert bounded cells/heap/timer lag, then restore 5,000 rows on the original PTY including output produced while parked and continued input/output. Separate headed/headless ACK-starvation tests recover one stalled stream without replacing its PTY. A capability-disabled run proves legacy hosts retain the prior lossy limit/TTL fallback. Unit tests cover exact-owner capability routing, raw-stream release, singleton side-effect facts with timed handoff cleanup, provider-authoritative snapshots, capacity-pressure backoff, full split-leaf remint reconciliation, truncation, and manual-disconnect queue fencing. Linux, Windows, mixed-version paired hosts, live SSH, and production-scale paired hosts remain gaps.",
|
||||
"coverageNotes": "Deterministic headed macOS runs launch an isolated Orca desktop server and a separate paired web client. A byte-identical headless run uses an isolated `orca serve` host. Both create six real paired host PTYs with bounded high-output scrollback, ordinary-park five client xterms without enabling the lossy retention budget, assert bounded cells/heap/timer lag, then restore 5,000 rows on the original PTY including output produced while parked and continued input/output. Separate headed/headless ACK-starvation tests recover one stalled stream without replacing its PTY. A capability-disabled run proves legacy hosts retain the prior lossy limit/TTL fallback. Unit tests cover exact-owner capability routing, raw-stream release, singleton side-effect facts with timed handoff cleanup, provider-authoritative snapshots, 128 active streams plus retry after a 129th-stream capacity rejection, capacity-pressure backoff, full split-leaf remint reconciliation, truncation, and manual-disconnect queue fencing. Linux, Windows, mixed-version paired hosts, live SSH, and production-scale paired hosts remain gaps.",
|
||||
"motivatingLinks": [
|
||||
"https://github.com/stablyai/orca/issues/8652",
|
||||
"https://github.com/stablyai/orca/pull/10625"
|
||||
],
|
||||
"invariant": "A host advertising terminal.paired-parking.v1 keeps the PTY and bounded authoritative history alive while an ordinary hidden-view park destroys the client xterm and releases its raw per-PTY stream. Reveal must restore up to the requested 5,000 rows, parked-time side effects/output, the same PTY identity, and continued input/output. Hosts without the capability must retain the existing limit/TTL force-parking fallback. A paired terminal stream whose delivery credits or command responses stop progressing must replace only that stream and repaint authoritative host output. Manual disconnect must retain pairing while preventing queued or passive calls and subscriptions from recreating transport until explicit Connect.",
|
||||
"oracle": "Run one byte-identical six-terminal oracle against an isolated headed desktop host and an isolated headless `orca serve` host. Stage at least 1,000,000 xterm cells, enable ordinary parking with the lossy retention budget disabled, require exactly one mounted manager and five parked tabs, at most 45% retained cells, no more than 16 MiB heap growth, and under 500 ms timer drift. While a tab is parked, require authoritative terminal.read to observe new PTY output; reveal it and require the original PTY, a marker within the requested 5,000-row history, the parked marker, and post-reveal input/output. Disable terminal.paired-parking.v1 and require the same oracle to fail before parking, while the legacy limit-one fallback separately passes. Also preserve truncated first paint, ACK-starved same-PTY stream recovery, and queued manual-disconnect fencing.",
|
||||
"oracle": "Run one byte-identical six-terminal oracle against an isolated headed desktop host and an isolated headless `orca serve` host. Stage at least 1,000,000 xterm cells, enable ordinary parking with the lossy retention budget disabled, require exactly one mounted manager and five parked tabs, at most 45% retained cells, no more than 16 MiB heap growth, and under 500 ms timer drift. While a tab is parked, require authoritative terminal.read to observe new PTY output; reveal it and require the original PTY, a marker within the requested 5,000-row history, the parked marker, and post-reveal input/output. Admit 128 active streams, reject the 129th as retryable, release one stream, then require the retry to attach and publish a snapshot without multiplying retained subscribers. Disable terminal.paired-parking.v1 and require the same oracle to fail before parking, while the legacy limit-one fallback separately passes. Also preserve truncated first paint, ACK-starved same-PTY stream recovery, and queued manual-disconnect fencing.",
|
||||
"commands": [
|
||||
"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/orca-runtime.test.ts src/main/runtime/rpc/terminal-multiplex.test.ts src/main/runtime/rpc/terminal-subscribe-buffer.test.ts src/renderer/src/components/terminal-pane/parked-terminal-byte-watcher.test.ts src/renderer/src/components/terminal-pane/terminal-side-effect-facts-handler.test.ts src/renderer/src/components/terminal-pane/pty-connection.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport.test.ts src/renderer/src/components/terminal-pane/terminal-hidden-view-parking.test.ts src/renderer/src/components/terminal-pane/terminal-hidden-worktree-retention.test.ts src/renderer/src/components/terminal-pane/terminal-parked-tab-watchers.test.ts src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts src/renderer/src/components/terminal-pane/terminal-parking-e2e-overrides.test.ts src/renderer/src/runtime/remote-runtime-terminal-stall-recovery.test.ts src/renderer/src/runtime/runtime-client-events.test.ts src/renderer/src/web/web-preload-api.test.ts src/main/ipc/runtime-environments.test.ts",
|
||||
"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/runtime/remote-runtime-terminal-parse-backpressure.test.ts",
|
||||
@@ -3088,7 +3088,7 @@
|
||||
"file": "src/main/runtime/rpc/terminal-multiplex.test.ts",
|
||||
"assertions": [
|
||||
"binary first paint remains valid when only retained history was truncated",
|
||||
"supports 44 active streams while retaining a bounded per-connection limit",
|
||||
"admits 128 active streams, rejects the 129th, and reuses released capacity",
|
||||
"reserves PTY wait capacity independently from active streams"
|
||||
]
|
||||
},
|
||||
@@ -3135,8 +3135,8 @@
|
||||
"platform": "macos",
|
||||
"command": "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/orca-runtime.test.ts src/main/runtime/rpc/terminal-multiplex.test.ts src/main/runtime/rpc/terminal-subscribe-buffer.test.ts src/renderer/src/components/terminal-pane/parked-terminal-byte-watcher.test.ts src/renderer/src/components/terminal-pane/terminal-side-effect-facts-handler.test.ts src/renderer/src/components/terminal-pane/pty-connection.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport.test.ts src/renderer/src/components/terminal-pane/terminal-hidden-view-parking.test.ts src/renderer/src/components/terminal-pane/terminal-hidden-worktree-retention.test.ts src/renderer/src/components/terminal-pane/terminal-parked-tab-watchers.test.ts src/renderer/src/components/terminal-pane/terminal-parked-watcher-reconciliation.test.ts src/renderer/src/components/terminal-pane/terminal-parking-e2e-overrides.test.ts src/renderer/src/runtime/remote-runtime-terminal-stall-recovery.test.ts src/renderer/src/runtime/runtime-client-events.test.ts src/renderer/src/web/web-preload-api.test.ts src/main/ipc/runtime-environments.test.ts",
|
||||
"result": "passed",
|
||||
"durationSeconds": 33.67,
|
||||
"summary": "Sixteen focused runtime, multiplex, parking, restore, side-effect, capacity, lifecycle, and queued-disconnect files passed 1,935 tests with one existing skip."
|
||||
"durationSeconds": 27.82,
|
||||
"summary": "Sixteen focused files passed 1,935 tests with one existing skip, including 128 active streams, retryable rejection at 129, released-slot snapshot recovery, and balanced subscriber cleanup."
|
||||
},
|
||||
{
|
||||
"date": "2026-07-29",
|
||||
@@ -3203,7 +3203,7 @@
|
||||
},
|
||||
"redGreenEvidence": {
|
||||
"status": "complete",
|
||||
"evidence": "The six-PTY ordinary-parking oracle was red in two independently observed stages: the parked paired remount first used detached attach and restored only rows 5,963–5,999, then the requested snapshot still lost to the current-screen replay until park reveals explicitly entered the capability-gated reattach coordinator. The candidate is green on headed and headless hosts, while disabling terminal.paired-parking.v1 makes the byte-identical oracle fail at its capability precondition and leaves the separately tested legacy lossy fallback green. With stalled-stream recovery disabled, the host cursor advanced but the client remained frozen; enabling it repainted the marker with the same PTY and tab."
|
||||
"evidence": "The six-PTY ordinary-parking oracle was red in two independently observed stages: the parked paired remount first used detached attach and restored only rows 5,963–5,999, then the requested snapshot still lost to the current-screen replay until park reveals explicitly entered the capability-gated reattach coordinator. The candidate is green on headed and headless hosts, while disabling terminal.paired-parking.v1 makes the byte-identical oracle fail at its capability precondition and leaves the separately tested legacy lossy fallback green. With stalled-stream recovery disabled, the host cursor advanced but the client remained frozen; enabling it repainted the marker with the same PTY and tab. Reverting the active-stream limit to 64 makes the exact 128-stream contract fail at 64; restoring 128 admits every intended stream, rejects 129, and reattaches it after one slot releases."
|
||||
},
|
||||
"performanceBudget": {
|
||||
"required": true,
|
||||
|
||||
@@ -4116,18 +4116,37 @@ describe('terminal multiplex RPC', () => {
|
||||
await harness.dispatchPromise
|
||||
})
|
||||
|
||||
it('supports 44 active streams while retaining a bounded per-connection limit', async () => {
|
||||
const harness = startDesktopMultiplexSubscribe()
|
||||
it('admits 128 active streams, rejects the 129th, and reuses released capacity', async () => {
|
||||
let dataSubscriberCount = 0
|
||||
let viewSubscriberCount = 0
|
||||
const harness = startDesktopMultiplexSubscribe({
|
||||
subscribeToTerminalData: vi.fn(() => {
|
||||
dataSubscriberCount += 1
|
||||
let released = false
|
||||
return () => {
|
||||
if (!released) {
|
||||
released = true
|
||||
dataSubscriberCount -= 1
|
||||
}
|
||||
}
|
||||
}),
|
||||
registerRemoteTerminalViewSubscriber: vi.fn(() => {
|
||||
viewSubscriberCount += 1
|
||||
let released = false
|
||||
return () => {
|
||||
if (!released) {
|
||||
released = true
|
||||
viewSubscriberCount -= 1
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
await vi.waitFor(() =>
|
||||
expect(harness.messages.some((message) => JSON.parse(message).result?.type === 'ready')).toBe(
|
||||
true
|
||||
)
|
||||
)
|
||||
for (
|
||||
let streamId = 1;
|
||||
streamId <= TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION + 1;
|
||||
streamId += 1
|
||||
) {
|
||||
const sendSubscribe = (streamId: number): void => {
|
||||
harness.handlers.get(0)?.(
|
||||
decodeTerminalStreamFrame(
|
||||
encodeTerminalStreamFrame({
|
||||
@@ -4144,6 +4163,14 @@ describe('terminal multiplex RPC', () => {
|
||||
)!
|
||||
)
|
||||
}
|
||||
expect(TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION).toBe(128)
|
||||
for (
|
||||
let streamId = 1;
|
||||
streamId <= TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION + 1;
|
||||
streamId += 1
|
||||
) {
|
||||
sendSubscribe(streamId)
|
||||
}
|
||||
|
||||
await vi.waitFor(() => {
|
||||
const results = harness.messages.map((message) => JSON.parse(message).result)
|
||||
@@ -4162,12 +4189,53 @@ describe('terminal multiplex RPC', () => {
|
||||
streamId: TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION + 1
|
||||
})
|
||||
})
|
||||
expect(dataSubscriberCount).toBe(TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION)
|
||||
expect(viewSubscriberCount).toBe(TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION)
|
||||
|
||||
harness.handlers.get(1)?.(
|
||||
decodeTerminalStreamFrame(
|
||||
encodeTerminalStreamFrame({
|
||||
opcode: TerminalStreamOpcode.Unsubscribe,
|
||||
streamId: 1,
|
||||
seq: TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION + 2,
|
||||
payload: new Uint8Array()
|
||||
})
|
||||
)!
|
||||
)
|
||||
await vi.waitFor(() => {
|
||||
expect(dataSubscriberCount).toBe(TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION - 1)
|
||||
expect(viewSubscriberCount).toBe(TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION - 1)
|
||||
expect(harness.handlers.has(1)).toBe(false)
|
||||
})
|
||||
|
||||
const retriedStreamId = TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION + 1
|
||||
sendSubscribe(retriedStreamId)
|
||||
await vi.waitFor(() => {
|
||||
const subscribedStreamIds = harness.messages
|
||||
.map((message) => JSON.parse(message).result)
|
||||
.filter((result) => result?.type === 'subscribed')
|
||||
.map((result) => result.streamId)
|
||||
expect(subscribedStreamIds).toContain(retriedStreamId)
|
||||
expect(
|
||||
harness.binaryFrames.some((bytes) => {
|
||||
const frame = decodeTerminalStreamFrame(bytes)
|
||||
return (
|
||||
frame?.streamId === retriedStreamId && frame.opcode === TerminalStreamOpcode.SnapshotEnd
|
||||
)
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
expect(dataSubscriberCount).toBe(TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION)
|
||||
expect(viewSubscriberCount).toBe(TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION)
|
||||
|
||||
harness.cleanups.get('terminal-multiplex:conn-desktop-first-paint')?.()
|
||||
await harness.dispatchPromise
|
||||
expect(dataSubscriberCount).toBe(0)
|
||||
expect(viewSubscriberCount).toBe(0)
|
||||
})
|
||||
|
||||
it('reserves PTY wait capacity independently from active streams', async () => {
|
||||
const activeStreamCount = 44
|
||||
const waitSignals: AbortSignal[] = []
|
||||
const resolveWaits: ((ptyId: string) => void)[] = []
|
||||
const runtime = stubRuntime({
|
||||
@@ -4211,13 +4279,13 @@ describe('terminal multiplex RPC', () => {
|
||||
)
|
||||
}
|
||||
|
||||
for (let streamId = 1; streamId <= 44; streamId += 1) {
|
||||
for (let streamId = 1; streamId <= activeStreamCount; streamId += 1) {
|
||||
sendSubscribe(streamId, `active-${streamId}`)
|
||||
}
|
||||
await vi.waitFor(() =>
|
||||
expect(
|
||||
harness.messages.filter((message) => JSON.parse(message).result?.type === 'subscribed')
|
||||
).toHaveLength(44)
|
||||
).toHaveLength(activeStreamCount)
|
||||
)
|
||||
|
||||
for (
|
||||
@@ -4225,12 +4293,13 @@ describe('terminal multiplex RPC', () => {
|
||||
offset <= TERMINAL_MULTIPLEX_MAX_PENDING_PTY_WAITS_PER_CONNECTION + 1;
|
||||
offset += 1
|
||||
) {
|
||||
sendSubscribe(44 + offset, `pending-${offset}`)
|
||||
sendSubscribe(activeStreamCount + offset, `pending-${offset}`)
|
||||
}
|
||||
await vi.waitFor(() =>
|
||||
expect(waitSignals).toHaveLength(TERMINAL_MULTIPLEX_MAX_PENDING_PTY_WAITS_PER_CONNECTION)
|
||||
)
|
||||
const rejectedStreamId = 45 + TERMINAL_MULTIPLEX_MAX_PENDING_PTY_WAITS_PER_CONNECTION
|
||||
const rejectedStreamId =
|
||||
activeStreamCount + TERMINAL_MULTIPLEX_MAX_PENDING_PTY_WAITS_PER_CONNECTION + 1
|
||||
await vi.waitFor(() => {
|
||||
const results = harness.messages.map((message) => JSON.parse(message).result)
|
||||
expect(results).toContainEqual({
|
||||
@@ -4247,18 +4316,14 @@ describe('terminal multiplex RPC', () => {
|
||||
await vi.waitFor(() => {
|
||||
const results = harness.messages.map((message) => JSON.parse(message).result)
|
||||
expect(results.filter((result) => result?.type === 'subscribed')).toHaveLength(
|
||||
TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION
|
||||
activeStreamCount + TERMINAL_MULTIPLEX_MAX_PENDING_PTY_WAITS_PER_CONNECTION
|
||||
)
|
||||
expect(
|
||||
results.filter(
|
||||
(result) =>
|
||||
result?.type === 'error' && result.message === 'terminal_stream_limit_exceeded'
|
||||
)
|
||||
).toHaveLength(
|
||||
45 +
|
||||
TERMINAL_MULTIPLEX_MAX_PENDING_PTY_WAITS_PER_CONNECTION -
|
||||
TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION
|
||||
)
|
||||
).toHaveLength(1)
|
||||
})
|
||||
|
||||
harness.cleanups.get('terminal-multiplex:conn-desktop-first-paint')?.()
|
||||
|
||||
@@ -7,7 +7,7 @@ export const TERMINAL_MULTIPLEX_ACK_TOTAL_MAX_WINDOW_BYTES = 8 * 1024 * 1024
|
||||
export const TERMINAL_MULTIPLEX_PENDING_MAX_BYTES = 256 * 1024
|
||||
export const TERMINAL_MULTIPLEX_ACK_BATCH_BYTES = 192 * 1024
|
||||
export const TERMINAL_MULTIPLEX_ACK_FLUSH_MS = 4
|
||||
// 64 covers observed 43-manager sessions with headroom while bounding per-stream replay buffers.
|
||||
export const TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION = 64
|
||||
// 128 covers large paired clients while fixed ACK windows and per-stream queues bound pressure.
|
||||
export const TERMINAL_MULTIPLEX_MAX_ACTIVE_STREAMS_PER_CONNECTION = 128
|
||||
export const TERMINAL_MULTIPLEX_MAX_PENDING_PTY_WAITS_PER_CONNECTION = 32
|
||||
export const TERMINAL_MULTIPLEX_STREAM_LIMIT_ERROR = 'terminal_stream_limit_exceeded'
|
||||
|
||||
Reference in New Issue
Block a user