refactor(wsl): move the WSL guest bundle sources out of src/relay (#27174)

* refactor(wsl): move the WSL guest bundle sources out of src/relay

The WSL hook relay and WSL browser network relay are built from src/relay but are
not part of the SSH relay. Move them, and the 46 relay modules the hook relay
bundles, to src/wsl-guest so the SSH relay can be deleted on its own. Pure
renames; the built bundles are byte-identical.

Adds a boundary test so nothing outside src/relay imports it except relay tests.

* fix(ci): exempt renamed files' existing lint debt in the changed-lines gate

* test(relay): keep the cast suppression on the wrapped line

* test(relay): only parse files that mention relay in the import boundary check

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
This commit is contained in:
OrcaWin
2026-10-10 02:46:27 -07:00
committed by GitHub
co-authored by m4air
parent 3c71318d5b
commit 31b2b17d9b
263 changed files with 443 additions and 301 deletions
+1
View File
@@ -17,6 +17,7 @@ on:
- 'src/main/ssh/ssh-hostile-host-*'
- 'src/main/ssh/sftp-*'
- 'src/relay/**'
- 'src/wsl-guest/**'
- 'src/shared/node-runtime-pin.ts'
- 'src/shared/orcad-artifacts.ts'
- 'config/scripts/build-orcad-*.mjs'
+1
View File
@@ -18,6 +18,7 @@ on:
- 'src/main/ssh/ssh-windows-host-*'
- 'src/main/ssh/ssh-session-command-audit.ts'
- 'src/relay/**'
- 'src/wsl-guest/**'
- 'src/packages/process-host/**'
- 'pnpm-workspace.yaml'
- 'src/shared/node-runtime-pin.ts'
+1 -1
View File
@@ -21,7 +21,7 @@
"src/renderer/src/**/*.worker.ts",
"src/cli/index.ts",
"src/relay/relay.ts",
"src/relay/wsl-agent-hook-relay.ts",
"src/wsl-guest/wsl-agent-hook-relay.ts",
"config/scripts/**/*.{mjs,cjs,js,ts}",
"config/build-plugins/**/*.ts",
"electron.vite.config.ts",
@@ -130,7 +130,6 @@ src/relay/ai-vault-service-spawn.ts
src/relay/external-automations-handler.ts
src/relay/fs-handler-git-fallback.ts
src/relay/fs-handler-list-files.ts
src/relay/preflight-handler.ts
src/relay/pty-shell-utils.ts
src/relay/subprocess-tree-termination.ts
src/relay/workspace-space-scan.ts
@@ -140,3 +139,4 @@ src/shared/fish-binary-requirement.ts
src/shared/process-table-snapshot-reader.ts
src/shared/pty-slave-line-discipline-echo.ts
src/shared/shell-process-readiness.ts
src/wsl-guest/preflight-handler.ts
+17 -17
View File
@@ -337,7 +337,7 @@
"invariant": "An ended tracked task stays pending until its wake-up and finishing turn end; missing wake-ups expire only after 60 idle seconds. Cancellation remains owned by the executing host, duplicate delivery cannot create a fresh completion veto, and fresh native rest can recover a lost finishing Stop.",
"oracle": "Replay captured hook ordering through production HTTP entry points and native ready bytes through the actual automation observer. Check local/relay state, cancellation/new-turn fencing, fresh versus retained rest, bounded task/timer ownership, and zero global status reads during indexed runtime waits.",
"commands": [
"ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/agent-hooks/server-claude-task-wakeup-lifecycle.test.ts src/main/runtime/tui-idle-claude-task-wakeup.test.ts src/main/runtime/claude-task-wakeup-indexed-read.test.ts src/shared/claude-owed-notification-resource-contract.test.ts src/shared/agent-hook-listener-claude-task-notification.test.ts src/shared/agent-hook-listener-claude-reordered-task-notification.test.ts src/relay/agent-hook-interrupt-reconciliation.test.ts src/main/ssh/ssh-agent-hook-interrupt-reconciliation.test.ts"
"ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/agent-hooks/server-claude-task-wakeup-lifecycle.test.ts src/main/runtime/tui-idle-claude-task-wakeup.test.ts src/main/runtime/claude-task-wakeup-indexed-read.test.ts src/shared/claude-owed-notification-resource-contract.test.ts src/shared/agent-hook-listener-claude-task-notification.test.ts src/shared/agent-hook-listener-claude-reordered-task-notification.test.ts src/wsl-guest/agent-hook-interrupt-reconciliation.test.ts src/main/ssh/ssh-agent-hook-interrupt-reconciliation.test.ts"
],
"testFiles": [
"src/main/agent-hooks/server-claude-task-wakeup-lifecycle.test.ts",
@@ -346,7 +346,7 @@
"src/shared/claude-owed-notification-resource-contract.test.ts",
"src/shared/agent-hook-listener-claude-task-notification.test.ts",
"src/shared/agent-hook-listener-claude-reordered-task-notification.test.ts",
"src/relay/agent-hook-interrupt-reconciliation.test.ts",
"src/wsl-guest/agent-hook-interrupt-reconciliation.test.ts",
"src/main/ssh/ssh-agent-hook-interrupt-reconciliation.test.ts"
],
"assertionRefs": [
@@ -376,7 +376,7 @@
"date": "2026-10-06",
"runner": "local",
"platform": "macos",
"command": "ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/agent-hooks/server-claude-task-wakeup-lifecycle.test.ts src/main/runtime/tui-idle-claude-task-wakeup.test.ts src/main/runtime/claude-task-wakeup-indexed-read.test.ts src/shared/claude-owed-notification-resource-contract.test.ts src/shared/agent-hook-listener-claude-task-notification.test.ts src/shared/agent-hook-listener-claude-reordered-task-notification.test.ts src/relay/agent-hook-interrupt-reconciliation.test.ts src/main/ssh/ssh-agent-hook-interrupt-reconciliation.test.ts",
"command": "ORCA_BACKGROUND_LAUNCH=1 node node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/agent-hooks/server-claude-task-wakeup-lifecycle.test.ts src/main/runtime/tui-idle-claude-task-wakeup.test.ts src/main/runtime/claude-task-wakeup-indexed-read.test.ts src/shared/claude-owed-notification-resource-contract.test.ts src/shared/agent-hook-listener-claude-task-notification.test.ts src/shared/agent-hook-listener-claude-reordered-task-notification.test.ts src/wsl-guest/agent-hook-interrupt-reconciliation.test.ts src/main/ssh/ssh-agent-hook-interrupt-reconciliation.test.ts",
"result": "passed",
"durationSeconds": 8.62,
"summary": "Exact stored command with repository Vitest configuration passes 148 tests across eight files including out-of-order launch/end/delivery controls. Includes real HTTP/relay ingress, 12 blank/padded Agent/Bash/Monitor IDs, captured-native-rest automation completion, duplicate/cancellation controls, owner/phase gates, timer/task bounds and indexed reads. Three upstream interrupt suites pass 28 more tests separately."
@@ -9838,7 +9838,7 @@
"invariant": "PreCompact is never registered and may never move a pane, because it fires before the compact is validated and an aborted compact emits it alone. Only a manual PostCompact may mark done, and it does so as a session boundary so completion-reactive consumers stay out of it; an automatic PostCompact claims nothing and writes no compact-scoped state, since its turn resumes and emits its own Stop. A completion either CLEARS a row or publishes nothing at all: it never creates a row, so a retired pane cannot be resurrected, and it never restates one it cannot clear, because a restated row loses restoredUnconfirmed and restarts the staleness clock. A manual /compact only completes at an idle prompt, so it retires a subagent that exists ONLY as a disk snapshot; live evidence -- a child observed in this runtime, an unclassifiable running background task, a registered session cron -- still holds the pane at working. A completion requires a valid provider prompt UUID, matches the pane owner by connection and provider session -- except on a restored row, which carries the previous connection and may carry no session at all -- and is applied at most once per compact. Ownership is enforced by the client that owns pane identity; a relay forwards and defers, so a cold relay cache cannot swallow the clearing event. A relay predating this contract forwards its own mapping, so ingest drops automatic envelopes, stamps the boundary on manual ones, carries the summarized turn label onto a promptless completion with or without a trigger, and classifies a trigger-stripped replay by payload state while still enforcing ownership. Kimi and other Claude-compatible vendors remain fail-closed.",
"oracle": "Replay the measured Claude compact streams through a harness gated on the real CLAUDE_EVENTS, plus local HTTP, relay, SSH, and persisted-restore transports. Require the successful manual stream to end done with sessionBoundary set, and require the aborted stream (PreCompact alone) to leave both an idle and a working pane exactly as it found them. Drive the restart shape end to end over loopback HTTP from a hydrated row: a pane held only by a snapshot child must reach done with an empty roster, while a pane a runtime child still owns must emit NOTHING and keep its row object identical, so receivedAt and restoredUnconfirmed both survive. Deliver the restored-row cases with the hydrated row still cached at completion time and assert the summarizer published nothing, so the restored-row branch is the branch under test. Require rejection of an automatic completion, a completion with absent prompt UUID, a foreign provider session, a duplicate, and a completion for a pane with no cached row; require an automatic completion to leave the consumed-compact map untouched. Require a relay with a COLD cache to forward the completion while the client still refuses to mint a row it never had. Require ingest to drop legacy automatic envelopes, stamp the boundary on legacy manual ones, carry the summarized turn label onto a promptless completion with and without a trigger, and leave a FINISHED pane finished when a trigger-stripped working replay arrives. Require the consumed-compact map to be empty after each pane-scoped teardown route and to follow a pane move. Assert registration membership, emitted event order, and the authoritative lastStatusByPaneKey snapshot.",
"commands": [
"pnpm exec vitest run --config config/vitest.config.ts src/main/claude/compact-status-registration.test.ts src/main/agent-hooks/manual-compact-hook-stream.test.ts src/main/agent-hooks/manual-compact-status-cleanup.test.ts src/shared/agent-hook-listener-claude-turn-state.test.ts src/shared/agent-hook-listener-claude-compatible-vendors.test.ts src/shared/agent-hook-relay.test.ts src/relay/agent-hook-server.test.ts src/main/agent-hooks/server-claude-normalization.test.ts src/main/agent-hooks/server-last-status-write.test.ts src/main/ssh/ssh-relay-session-agent-hooks.integration.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/claude/compact-status-registration.test.ts src/main/agent-hooks/manual-compact-hook-stream.test.ts src/main/agent-hooks/manual-compact-status-cleanup.test.ts src/shared/agent-hook-listener-claude-turn-state.test.ts src/shared/agent-hook-listener-claude-compatible-vendors.test.ts src/shared/agent-hook-relay.test.ts src/wsl-guest/agent-hook-server.test.ts src/main/agent-hooks/server-claude-normalization.test.ts src/main/agent-hooks/server-last-status-write.test.ts src/main/ssh/ssh-relay-session-agent-hooks.integration.test.ts --reporter=dot",
"Manual Claude Code interactive compact under a PTY: register every hook to a logging script, run one turn then /compact to observe the abort stream, then seven turns and /compact to observe the success stream; compare source, trigger, session_id, and prompt_id across both"
],
"testFiles": [
@@ -9848,7 +9848,7 @@
"src/shared/agent-hook-listener-claude-turn-state.test.ts",
"src/shared/agent-hook-listener-claude-compatible-vendors.test.ts",
"src/shared/agent-hook-relay.test.ts",
"src/relay/agent-hook-server.test.ts",
"src/wsl-guest/agent-hook-server.test.ts",
"src/main/agent-hooks/server-claude-normalization.test.ts",
"src/main/agent-hooks/server-last-status-write.test.ts",
"src/main/ssh/ssh-relay-session-agent-hooks.integration.test.ts"
@@ -9896,7 +9896,7 @@
"date": "2026-08-18",
"runner": "local",
"platform": "macos",
"command": "pnpm exec vitest run --config config/vitest.config.ts src/main/claude/compact-status-registration.test.ts src/main/agent-hooks/manual-compact-hook-stream.test.ts src/main/agent-hooks/manual-compact-status-cleanup.test.ts src/shared/agent-hook-listener-claude-turn-state.test.ts src/shared/agent-hook-listener-claude-compatible-vendors.test.ts src/shared/agent-hook-relay.test.ts src/relay/agent-hook-server.test.ts src/main/agent-hooks/server-claude-normalization.test.ts src/main/agent-hooks/server-last-status-write.test.ts src/main/ssh/ssh-relay-session-agent-hooks.integration.test.ts --reporter=dot",
"command": "pnpm exec vitest run --config config/vitest.config.ts src/main/claude/compact-status-registration.test.ts src/main/agent-hooks/manual-compact-hook-stream.test.ts src/main/agent-hooks/manual-compact-status-cleanup.test.ts src/shared/agent-hook-listener-claude-turn-state.test.ts src/shared/agent-hook-listener-claude-compatible-vendors.test.ts src/shared/agent-hook-relay.test.ts src/wsl-guest/agent-hook-server.test.ts src/main/agent-hooks/server-claude-normalization.test.ts src/main/agent-hooks/server-last-status-write.test.ts src/main/ssh/ssh-relay-session-agent-hooks.integration.test.ts --reporter=dot",
"result": "passed",
"durationSeconds": 3.28,
"summary": "Ten files and 116 tests passed across registration, listener, local HTTP, relay, SSH, persistence, and cleanup. Reverting only the four source files to the merge base first turned 12 of 21 registration-gated tests and 8 of 12 relay/ingest tests red, so the suite observes its own failure rather than passing over unreachable code. Every assertion was additionally mutation-tested: 18 of 18 targeted mutations -- including reaping runtime children as well as snapshot ones, restating a pane live evidence still holds, re-enforcing compact ownership on the relay, and dropping the consumed-compact map from any one teardown route -- are caught."
@@ -12799,17 +12799,17 @@
"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/pty-pending-projection-admissions.test.ts src/main/ipc/ssh-pty-legacy-projection.test.ts src/main/ipc/ssh-pty-model-admission.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/orca-runtime-path-candidate-history.test.ts",
"pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/orca-runtime.test.ts src/main/runtime/orca-runtime-tail-wait-memo.test.ts",
"pnpm exec vitest run --config config/vitest.config.ts src/main/providers/ssh-pty-provider.test.ts src/main/providers/ssh-pty-source-delivery-ledger.test.ts src/main/ssh/ssh-pty-retired-source-deliveries.test.ts src/main/ssh/ssh-relay-session.test.ts src/main/ssh/ssh-relay-session-data-delivery.test.ts src/main/ssh/ssh-relay-session-recovery-races.test.ts src/main/ssh/ssh-relay-session-incarnation.test.ts src/main/ssh/ssh-relay-session-reconnect-incarnation.test.ts src/main/ssh/ssh-relay-session-terminal-error.test.ts src/main/ssh/ssh-pty-recovery-retention-budget.test.ts src/relay/protocol-backpressure.test.ts src/relay/pty-source-credit-ledger.test.ts src/relay/pty-source-credit-scheduler.test.ts src/relay/relay-pty-source-publication.test.ts src/relay/ssh-pty-source-credit-adapter.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/providers/ssh-pty-provider.test.ts src/main/providers/ssh-pty-source-delivery-ledger.test.ts src/main/ssh/ssh-pty-retired-source-deliveries.test.ts src/main/ssh/ssh-relay-session.test.ts src/main/ssh/ssh-relay-session-data-delivery.test.ts src/main/ssh/ssh-relay-session-recovery-races.test.ts src/main/ssh/ssh-relay-session-incarnation.test.ts src/main/ssh/ssh-relay-session-reconnect-incarnation.test.ts src/main/ssh/ssh-relay-session-terminal-error.test.ts src/main/ssh/ssh-pty-recovery-retention-budget.test.ts src/wsl-guest/protocol-backpressure.test.ts src/relay/pty-source-credit-ledger.test.ts src/relay/pty-source-credit-scheduler.test.ts src/relay/relay-pty-source-publication.test.ts src/relay/ssh-pty-source-credit-adapter.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/ssh-pty-model-admission.test.ts src/main/ipc/ssh-pty-output-model-migration.test.ts src/main/ssh/ssh-relay-session-model-migration.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/relay/git-response-stream-ownership.test.ts src/relay/pty-handler-output-streaming.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/providers/ssh-pty-notification-routing.test.ts src/main/providers/ssh-pty-provider-agent-session-create-operation.test.ts src/main/providers/ssh-pty-provider-exit-race.test.ts src/main/providers/ssh-pty-provider-reattach-incarnation.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/relay/relay-pty-source-recovery-interleavings.test.ts src/relay/relay-pty-source-recovery-completion.test.ts src/relay/relay-pty-source-restore-retry.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/relay/dispatcher.test.ts src/relay/pty-handler-source-publication.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/wsl-guest/dispatcher.test.ts src/relay/pty-handler-source-publication.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/ssh-relay-session-recovery-races.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/ssh-pty-legacy-projection.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/ssh-pty-remote-source-range-consumers.test.ts src/main/runtime/rpc/terminal-source-range-ledger.test.ts src/main/runtime/rpc/terminal-multiplex-source-range-admission.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/shared/pty-consumer-session.test.ts src/main/ipc/ssh-pty-output-intake.test.ts src/main/ipc/ssh-pty-output-exit-deadline.test.ts src/main/ipc/ssh-pty-remote-source-range-consumers.test.ts src/main/runtime/rpc/terminal-multiplex-source-range-admission.test.ts src/main/ssh/ssh-multiplexer-transport-writer.test.ts src/main/ssh/ssh-channel-multiplexer.test.ts src/main/ssh/ssh-relay-deploy.test.ts src/relay/dispatcher-client-writer.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/shared/pty-consumer-session.test.ts src/main/ipc/ssh-pty-output-intake.test.ts src/main/ipc/ssh-pty-output-generation-guard.test.ts src/main/ipc/ssh-pty-output-exit-deadline.test.ts src/main/ipc/ssh-pty-remote-source-range-consumers.test.ts src/main/runtime/rpc/terminal-multiplex-source-range-admission.test.ts src/main/ssh/ssh-multiplexer-transport-writer.test.ts src/main/ssh/ssh-channel-multiplexer.test.ts src/main/ssh/ssh-channel-multiplexer-backpressure.test.ts src/main/ssh/ssh-relay-deploy.test.ts src/relay/dispatcher-client-writer.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/shared/pty-consumer-session.test.ts src/main/ipc/ssh-pty-output-intake.test.ts src/main/ipc/ssh-pty-output-exit-deadline.test.ts src/main/ipc/ssh-pty-remote-source-range-consumers.test.ts src/main/runtime/rpc/terminal-multiplex-source-range-admission.test.ts src/main/ssh/ssh-multiplexer-transport-writer.test.ts src/main/ssh/ssh-channel-multiplexer.test.ts src/main/ssh/ssh-relay-deploy.test.ts src/wsl-guest/dispatcher-client-writer.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/shared/pty-consumer-session.test.ts src/main/ipc/ssh-pty-output-intake.test.ts src/main/ipc/ssh-pty-output-generation-guard.test.ts src/main/ipc/ssh-pty-output-exit-deadline.test.ts src/main/ipc/ssh-pty-remote-source-range-consumers.test.ts src/main/runtime/rpc/terminal-multiplex-source-range-admission.test.ts src/main/ssh/ssh-multiplexer-transport-writer.test.ts src/main/ssh/ssh-channel-multiplexer.test.ts src/main/ssh/ssh-channel-multiplexer-backpressure.test.ts src/main/ssh/ssh-relay-deploy.test.ts src/wsl-guest/dispatcher-client-writer.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/relay/relay-pty-source-restore-retry.test.ts src/main/ssh/ssh-relay-session-recovery-races.test.ts src/main/runtime/rpc/terminal-source-range-ledger.test.ts src/main/runtime/rpc/terminal-multiplex-source-range-admission.test.ts src/main/ssh/ssh-multiplexer-transport-writer.test.ts src/main/ssh/ssh-channel-multiplexer-backpressure.test.ts src/main/providers/ssh-pty-notification-routing.test.ts src/main/providers/ssh-pty-source-delivery-ledger.test.ts src/main/ssh/ssh-pty-retired-source-deliveries.test.ts src/main/providers/ssh-pty-provider-agent-session-create-operation.test.ts src/main/providers/ssh-pty-provider-exit-race.test.ts src/main/ipc/ssh-pty-output-exit-deadline.test.ts src/main/ipc/ssh-pty-remote-source-range-consumers.test.ts --reporter=dot",
"pnpm exec vitest run src/main/providers/ssh-pty-notification-routing.test.ts src/main/providers/ssh-pty-provider-exit-race.test.ts src/main/ssh/ssh-relay-session-data-delivery.test.ts src/main/ssh/ssh-relay-session-recovery-races.test.ts src/main/ssh/ssh-relay-session-reconnect-incarnation.test.ts src/main/ssh/ssh-relay-session-incarnation.test.ts src/main/ssh/ssh-relay-session.test.ts --reporter=dot",
"pnpm exec vitest run --config config/vitest.config.ts src/main/ssh/ssh-relay-session.test.ts src/main/ssh/ssh-relay-session-data-delivery.test.ts src/main/ssh/ssh-relay-session-reconnect-incarnation.test.ts src/main/ssh/ssh-relay-deploy.test.ts src/relay/ssh-pty-consumer-session-adapter.test.ts src/main/persistence-ssh-targets-and-pane-keys.test.ts --reporter=dot",
@@ -12846,7 +12846,7 @@
"src/main/ssh/ssh-relay-session-terminal-error.test.ts",
"src/main/ssh/ssh-pty-recovery-retention-budget.test.ts",
"src/main/ssh/ssh-pty-retired-source-deliveries.test.ts",
"src/relay/protocol-backpressure.test.ts",
"src/wsl-guest/protocol-backpressure.test.ts",
"src/relay/git-response-stream-ownership.test.ts",
"src/relay/pty-handler-output-streaming.test.ts",
"src/relay/pty-source-credit-ledger.test.ts",
@@ -12855,7 +12855,7 @@
"src/relay/relay-pty-source-recovery-interleavings.test.ts",
"src/relay/relay-pty-source-recovery-completion.test.ts",
"src/relay/relay-pty-source-restore-retry.test.ts",
"src/relay/dispatcher.test.ts",
"src/wsl-guest/dispatcher.test.ts",
"src/relay/pty-handler-source-publication.test.ts",
"src/relay/ssh-pty-source-credit-adapter.test.ts",
"src/shared/pty-consumer-session.test.ts",
@@ -12872,7 +12872,7 @@
"src/main/ssh/ssh-channel-multiplexer-backpressure.test.ts",
"src/main/ssh/ssh-relay-deploy.test.ts",
"src/main/persistence-ssh-targets-and-pane-keys.test.ts",
"src/relay/dispatcher-client-writer.test.ts",
"src/wsl-guest/dispatcher-client-writer.test.ts",
"src/main/agent-hooks/wsl-hook-relay-sentinel.test.ts",
"tests/e2e/ssh-docker-relay-perf.spec.ts"
],
@@ -13029,7 +13029,7 @@
]
},
{
"file": "src/relay/dispatcher.test.ts",
"file": "src/wsl-guest/dispatcher.test.ts",
"assertions": [
"a saturated legacy primary remains required backpressure and is never detached as an additional subscriber"
]
@@ -13063,7 +13063,7 @@
]
},
{
"file": "src/relay/protocol-backpressure.test.ts",
"file": "src/wsl-guest/protocol-backpressure.test.ts",
"assertions": [
"relay decoder accepts one maximum frame plus 1 MiB partial input and rejects one extra byte",
"a throwing continuation clears retained input, releases one pause epoch, and publishes one typed ownership error"
@@ -13196,7 +13196,7 @@
]
},
{
"file": "src/relay/dispatcher-client-writer.test.ts",
"file": "src/wsl-guest/dispatcher-client-writer.test.ts",
"assertions": [
"encoded producer frames stay below high-water-minus-reserve capacity",
"one fixed-size filesystem compatibility frame is admitted only on an empty sink",
@@ -13250,7 +13250,7 @@
"date": "2026-07-29",
"runner": "local",
"platform": "macos",
"command": "pnpm exec vitest run --config config/vitest.config.ts src/main/providers/ssh-pty-provider.test.ts src/main/providers/ssh-pty-source-delivery-ledger.test.ts src/main/ssh/ssh-pty-retired-source-deliveries.test.ts src/main/ssh/ssh-relay-session.test.ts src/main/ssh/ssh-relay-session-data-delivery.test.ts src/main/ssh/ssh-relay-session-recovery-races.test.ts src/main/ssh/ssh-relay-session-incarnation.test.ts src/main/ssh/ssh-relay-session-reconnect-incarnation.test.ts src/main/ssh/ssh-relay-session-terminal-error.test.ts src/main/ssh/ssh-pty-recovery-retention-budget.test.ts src/relay/protocol-backpressure.test.ts src/relay/pty-source-credit-ledger.test.ts src/relay/pty-source-credit-scheduler.test.ts src/relay/relay-pty-source-publication.test.ts src/relay/ssh-pty-source-credit-adapter.test.ts --reporter=dot",
"command": "pnpm exec vitest run --config config/vitest.config.ts src/main/providers/ssh-pty-provider.test.ts src/main/providers/ssh-pty-source-delivery-ledger.test.ts src/main/ssh/ssh-pty-retired-source-deliveries.test.ts src/main/ssh/ssh-relay-session.test.ts src/main/ssh/ssh-relay-session-data-delivery.test.ts src/main/ssh/ssh-relay-session-recovery-races.test.ts src/main/ssh/ssh-relay-session-incarnation.test.ts src/main/ssh/ssh-relay-session-reconnect-incarnation.test.ts src/main/ssh/ssh-relay-session-terminal-error.test.ts src/main/ssh/ssh-pty-recovery-retention-budget.test.ts src/wsl-guest/protocol-backpressure.test.ts src/relay/pty-source-credit-ledger.test.ts src/relay/pty-source-credit-scheduler.test.ts src/relay/relay-pty-source-publication.test.ts src/relay/ssh-pty-source-credit-adapter.test.ts --reporter=dot",
"result": "passed",
"durationSeconds": 2.24,
"summary": "Sixteen deterministic SSH relay/session/source/decoder files passed 192 tests, including exit-sealed private recovery, retained stale-transfer cancellation authority, exact private-frame proof watermarks, one retirement record across 10,000 same-PTY token rotations, stale-owner fallback, and scheduler rejection isolation; no live topology was exercised."
+2 -2
View File
@@ -289,8 +289,8 @@ for (const platform of RELAY_BUILD_PLATFORMS) {
// so a single platform-independent bundle suffices; it ships inside the
// Windows app via the same out/relay extraResources mapping.
{
const wslHookEntry = join(ROOT, 'src', 'relay', 'wsl-agent-hook-relay.ts')
const wslBrowserNetworkEntry = join(ROOT, 'src', 'relay', 'wsl-browser-network-relay.ts')
const wslHookEntry = join(ROOT, 'src', 'wsl-guest', 'wsl-agent-hook-relay.ts')
const wslBrowserNetworkEntry = join(ROOT, 'src', 'wsl-guest', 'wsl-browser-network-relay.ts')
const outDir = join(OUT_ROOT, 'wsl')
mkdirSync(outDir, { recursive: true })
await build({
@@ -232,11 +232,12 @@ export function collectBaseLineBlocks(root, comparisonBase, files = null) {
// Why: in a split, the moved code's base text lives in the ORIGINAL file, which is
// often deleted or renamed away. Deleted paths never reach the changed-file list
// (it filters to ACMRTUB), so read every path the diff touches, deletions included.
// --no-renames: a rename otherwise lists only its new path, which the base lacks.
const paths =
files ??
splitNullDelimited(runGit(root, ['diff', '--name-only', '-z', comparisonBase, '--'])).filter(
(file) => SOURCE_FILE_PATTERN.test(file)
)
splitNullDelimited(
runGit(root, ['diff', '--name-only', '--no-renames', '-z', comparisonBase, '--'])
).filter((file) => SOURCE_FILE_PATTERN.test(file))
const blocks = []
for (const file of paths) {
const result = spawnSync('git', ['show', `${comparisonBase}:${file}`], {
@@ -1,10 +1,12 @@
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import path from 'node:path'
import { describe, expect, it } from 'vitest'
import { runProcessSync } from '@orca/process-host'
import { resolveOxlintInvocation } from './oxlint-cli-invocation.mjs'
import {
OXLINT_SCANS,
collectBaseLineBlocks,
diagnosticTouchesAddedLines,
isUnloadedPluginDirectiveUnusedWarning,
isMovedCode,
@@ -124,6 +126,31 @@ describe('moved-code exemption', () => {
it('never exempts an empty highlight', () => {
expect(isMovedCode(['', ' '], [['a()']])).toBe(false)
})
it('reads the base text of a file that was renamed away', () => {
const repo = mkdtempSync(path.join(tmpdir(), 'changed-code-quality-rename-'))
try {
const git = (...args) => {
const result = runProcessSync({
program: 'git',
args: ['-c', 'commit.gpgsign=false', ...args],
cwd: repo
})
expect(result.code, result.stderr).toBe(0)
return result.stdout.trim()
}
git('init', '--quiet')
writeFileSync(path.join(repo, 'old.ts'), 'const moved = value as Thing\n')
git('add', 'old.ts')
git('-c', 'user.name=t', '-c', 'user.email=t@example.invalid', 'commit', '-qm', 'base')
const base = git('rev-parse', 'HEAD')
git('mv', 'old.ts', 'new.ts')
expect(collectBaseLineBlocks(repo, base)).toEqual([['const moved = value as Thing']])
} finally {
rmSync(repo, { recursive: true, force: true })
}
})
})
describe('unloaded plugin directive unused warning', () => {
+31 -31
View File
@@ -4697,11 +4697,11 @@
"src/preload/usage-provider-api.test.ts": 47,
"src/relay/agent-exec-handler-windows.test.ts": 71,
"src/relay/agent-exec-handler.test.ts": 157,
"src/relay/agent-hook-envelope-publication.test.ts": 351,
"src/relay/agent-hook-integration.test.ts": 848,
"src/wsl-guest/agent-hook-envelope-publication.test.ts": 351,
"src/wsl-guest/agent-hook-integration.test.ts": 848,
"src/relay/agent-hook-retired-pane-suppression.test.ts": 451,
"src/relay/agent-hook-server-codex-subagent-transcript.test.ts": 1327,
"src/relay/agent-hook-server.test.ts": 1396,
"src/wsl-guest/agent-hook-server-codex-subagent-transcript.test.ts": 1327,
"src/wsl-guest/agent-hook-server.test.ts": 1396,
"src/relay/agent-status-store-relay-context.test.ts": 144,
"src/relay/ai-vault-handler.test.ts": 689,
"src/relay/ai-vault-opencode-reader.test.ts": 405,
@@ -4710,19 +4710,19 @@
"src/relay/ai-vault-service-restart-policy.test.ts": 69,
"src/relay/ai-vault-service-spawn.test.ts": 54,
"src/relay/context.test.ts": 39,
"src/relay/dispatcher-capacity-degradation.test.ts": 194,
"src/relay/dispatcher-client-close-cause.test.ts": 194,
"src/relay/dispatcher-client-writer.test.ts": 92,
"src/relay/dispatcher-frame-guard-regressions.test.ts": 169,
"src/relay/dispatcher-json-payload.test.ts": 122,
"src/relay/dispatcher-notification-ownership.test.ts": 140,
"src/relay/dispatcher-per-connection-state-baseline.test.ts": 146,
"src/relay/dispatcher-rpc-cancel-id-coercion.test.ts": 134,
"src/relay/dispatcher-silent-client-reaper.test.ts": 213,
"src/relay/dispatcher-structured-error.test.ts": 136,
"src/relay/dispatcher-timeout.test.ts": 210,
"src/relay/dispatcher-writer-admission.test.ts": 161,
"src/relay/dispatcher.test.ts": 306,
"src/wsl-guest/dispatcher-capacity-degradation.test.ts": 194,
"src/wsl-guest/dispatcher-client-close-cause.test.ts": 194,
"src/wsl-guest/dispatcher-client-writer.test.ts": 92,
"src/wsl-guest/dispatcher-frame-guard-regressions.test.ts": 169,
"src/wsl-guest/dispatcher-json-payload.test.ts": 122,
"src/wsl-guest/dispatcher-notification-ownership.test.ts": 140,
"src/wsl-guest/dispatcher-per-connection-state-baseline.test.ts": 146,
"src/wsl-guest/dispatcher-rpc-cancel-id-coercion.test.ts": 134,
"src/wsl-guest/dispatcher-silent-client-reaper.test.ts": 213,
"src/wsl-guest/dispatcher-structured-error.test.ts": 136,
"src/wsl-guest/dispatcher-timeout.test.ts": 210,
"src/wsl-guest/dispatcher-writer-admission.test.ts": 161,
"src/wsl-guest/dispatcher.test.ts": 306,
"src/relay/external-automation-provider-catalog.test.ts": 59,
"src/relay/external-automations-handler-log-path.test.ts": 46,
"src/relay/external-automations-handler.test.ts": 77,
@@ -4788,20 +4788,20 @@
"src/relay/hermes-run-correlation.test.ts": 44,
"src/relay/hermes-run-history.test.ts": 85,
"src/relay/integration.test.ts": 1877,
"src/relay/legacy-relay-publication-ledger.test.ts": 36,
"src/wsl-guest/legacy-relay-publication-ledger.test.ts": 36,
"src/relay/managed-hook-installer.test.ts": 117,
"src/relay/node-pty-binding-survey.test.ts": 246,
"src/relay/node-pty-unavailable-diagnosis.test.ts": 185,
"src/relay/omp-config-root.test.ts": 152,
"src/wsl-guest/omp-config-root.test.ts": 152,
"src/relay/omp-fresh-launch-environment.test.ts": 600,
"src/relay/plugin-overlay-env.test.ts": 60,
"src/relay/plugin-overlay.test.ts": 164,
"src/relay/plugin-source-limit.test.ts": 41,
"src/wsl-guest/plugin-overlay-env.test.ts": 60,
"src/wsl-guest/plugin-overlay.test.ts": 164,
"src/wsl-guest/plugin-source-limit.test.ts": 41,
"src/relay/port-scan-handler.test.ts": 190,
"src/relay/preflight-handler.test.ts": 97,
"src/relay/protocol-backpressure.test.ts": 87,
"src/relay/protocol-handshake.test.ts": 63,
"src/relay/protocol-json-payload.test.ts": 188,
"src/wsl-guest/preflight-handler.test.ts": 97,
"src/wsl-guest/protocol-backpressure.test.ts": 87,
"src/wsl-guest/protocol-handshake.test.ts": 63,
"src/wsl-guest/protocol-json-payload.test.ts": 188,
"src/relay/pty-handler-attach-replay.test.ts": 414,
"src/relay/pty-handler-dispose-lifecycle.test.ts": 481,
"src/relay/pty-handler-grace-timer.test.ts": 346,
@@ -4830,14 +4830,14 @@
"src/relay/pty-source-credit-scheduler.test.ts": 106,
"src/relay/pty-source-sent-boundaries.test.ts": 181,
"src/relay/relay-bundled-ripgrep.test.ts": 151,
"src/relay/relay-command-env.test.ts": 96,
"src/wsl-guest/relay-command-env.test.ts": 96,
"src/relay/relay-daemon-fatal-reap.test.ts": 1107,
"src/relay/relay-diagnostic-log.test.ts": 62,
"src/relay/relay-endpoint-credential-publication.test.ts": 2995,
"src/relay/relay-filesystem-watch-registry.test.ts": 344,
"src/relay/relay-grace-branch.test.ts": 52,
"src/relay/relay-handshake-roundtrip.test.ts": 495,
"src/relay/relay-hot-path-operation-counts.test.ts": 211,
"src/wsl-guest/relay-hot-path-operation-counts.test.ts": 211,
"src/relay/relay-launch-options.test.ts": 48,
"src/relay/relay-oversized-notification-survival.test.ts": 335,
"src/relay/relay-primary-channel.test.ts": 104,
@@ -4882,9 +4882,9 @@
"src/relay/workspace-snapshot-publication.test.ts": 126,
"src/relay/workspace-space-scan-du-capacity.test.ts": 110,
"src/relay/workspace-space-scan.test.ts": 66,
"src/relay/wsl-agent-hook-relay.test.ts": 317,
"src/relay/wsl-hook-fs-bridge.test.ts": 39,
"src/relay/wsl-install-plugins-handler.test.ts": 161,
"src/wsl-guest/wsl-agent-hook-relay.test.ts": 317,
"src/wsl-guest/wsl-hook-fs-bridge.test.ts": 39,
"src/wsl-guest/wsl-install-plugins-handler.test.ts": 161,
"src/renderer/src/app-shell/app-command-handlers-tab-rename.test.ts": 1093,
"src/renderer/src/app-shell/app-command-handlers-workspace-delete.test.ts": 97,
"src/renderer/src/app-shell/app-root-surface-settings.test.tsx": 721,
+1 -1
View File
@@ -17,7 +17,7 @@ const BUILD_PREFIXES = [
'.github/actions/prepare-orcad-prebuilds/'
]
// A remote target's OS does not identify the client platform that builds its commands.
const CROSS_HOST_PREFIXES = ['src/main/ssh/', 'src/main/providers/', 'src/relay/']
const CROSS_HOST_PREFIXES = ['src/main/ssh/', 'src/main/providers/', 'src/relay/', 'src/wsl-guest/']
const PLATFORM_PREFIXES = [
'src/main/persistence/',
'src/main/sqlite/',
+1
View File
@@ -250,6 +250,7 @@ const MANAGED_HOOK_PREFIXES = [
'src/packages/process-host/',
'config/scripts/build-relay',
'src/relay/',
'src/wsl-guest/',
'src/shared/agent-hook',
'src/main/agent-hooks/'
]
@@ -86,6 +86,7 @@ describe('PR E2E gate contract', () => {
'src/main/providers/ssh-',
'src/main/ipc/pty',
'src/relay/',
'src/wsl-guest/',
'src/shared/ssh-',
'src/renderer/src/store/slices/direct-ssh-',
'src/renderer/src/components/terminal-pane/remote-runtime-'
+2 -2
View File
@@ -189,7 +189,7 @@ export const PR_E2E_SOURCE_ROUTES = [
specs: ['tests/e2e/ssh-localhost.spec.ts'],
matches: (file) =>
isProductSource(file) &&
/^src\/(?:relay\/(?:agent-hook|relay-agent-hook-runtime|plugin-overlay)|main\/(?:agent-hooks\/|ssh\/ssh-relay-session\.ts$)|shared\/agent-hook)/.test(
/^src\/(?:relay\/(?:agent-hook|relay-agent-hook-runtime|plugin-overlay)|wsl-guest\/(?:agent-hook|plugin-overlay)|main\/(?:agent-hooks\/|ssh\/ssh-relay-session\.ts$)|shared\/agent-hook)/.test(
file
)
},
@@ -256,7 +256,7 @@ export const PR_E2E_SOURCE_ROUTES = [
// admission that decide whether a pane rebinds live in the renderer store.
matches: (file) =>
isProductSource(file) &&
/^(?:src\/main\/ssh\/|src\/main\/providers\/ssh-|src\/main\/ipc\/(?:ssh-|pty)|src\/main\/runtime\/(?:public-ssh-state|ssh-file-explorer-chunk-read)\.ts|src\/relay\/|src\/shared\/(?:ssh-|skill-ssh-relay-contract)|src\/renderer\/src\/startup\/(?:ssh-startup-reconnect|startup-ssh-connection-restore)\.ts|src\/renderer\/src\/store\/slices\/(?:ssh|direct-ssh-)|src\/renderer\/src\/components\/terminal-pane\/(?:pty-|ssh-|remote-runtime-|terminal-parked-pty))/.test(
/^(?:src\/main\/ssh\/|src\/main\/providers\/ssh-|src\/main\/ipc\/(?:ssh-|pty)|src\/main\/runtime\/(?:public-ssh-state|ssh-file-explorer-chunk-read)\.ts|src\/relay\/|src\/wsl-guest\/|src\/shared\/(?:ssh-|skill-ssh-relay-contract)|src\/renderer\/src\/startup\/(?:ssh-startup-reconnect|startup-ssh-connection-restore)\.ts|src\/renderer\/src\/store\/slices\/(?:ssh|direct-ssh-)|src\/renderer\/src\/components\/terminal-pane\/(?:pty-|ssh-|remote-runtime-|terminal-parked-pty))/.test(
file
)
},
@@ -8,7 +8,7 @@ import {
encodePreparedJsonRpcFrame,
prepareJsonRpcPayload,
type JsonRpcNotification
} from '../../src/relay/protocol'
} from '../../src/wsl-guest/protocol'
type BenchmarkCase = {
name: string
@@ -65,7 +65,7 @@ function waitForExit(proc) {
async function loadProtocol(bundleDir) {
const outfile = join(bundleDir, 'relay-protocol.cjs')
await build({
entryPoints: [resolve('src/relay/protocol.ts')],
entryPoints: [resolve('src/wsl-guest/protocol.ts')],
bundle: true,
platform: 'node',
format: 'cjs',
@@ -38,8 +38,8 @@ it('selects the localhost journey for its remote hook authorities', () => {
const spec = 'tests/e2e/ssh-localhost.spec.ts'
for (const file of [
'src/relay/relay-agent-hook-runtime.ts',
'src/relay/agent-hook-server.ts',
'src/relay/plugin-overlay.ts',
'src/wsl-guest/agent-hook-server.ts',
'src/wsl-guest/plugin-overlay.ts',
'src/main/agent-hooks/server.ts',
'src/main/ssh/ssh-relay-session.ts',
'src/shared/agent-hook-relay.ts'
@@ -0,0 +1,72 @@
import { globSync, readFileSync } from 'node:fs'
import path from 'node:path'
import { expect, it } from 'vitest'
import { collectModuleSpecifiers } from './static-module-specifiers.mjs'
import { isTestOnlySourcePath } from './test-only-source-path.mjs'
const ROOT = path.resolve(import.meta.dirname, '..', '..')
const RELAY_SOURCE = 'src/relay'
// Why: the SSH relay is being retired; only its own tests may reach into it, and this list only shrinks.
const RELAY_TEST_IMPORTERS = [
'src/main/ai-vault-search/session-search-scope-entry-points.test.ts',
'src/main/ai-vault/antigravity-index-file-admission.test.ts',
'src/main/ai-vault/remote-session-large-transcripts.test.ts',
'src/main/git/git-network-safety-real-git.test.ts',
'src/main/git/source-control/blob-absence-real-git.test.ts',
'src/main/git/source-control/bulk-pathspec-stdin.test.ts',
'src/main/git/source-control/staging-discard-index-safety.test.ts',
'src/main/git/status-branch-compare-real-ref.test.ts',
'src/main/git/status-branch-line-total-relay-parity.test.ts',
'src/main/git/status-conflict-overlap.bench.test.ts',
'src/main/git/worktree-rebase-update-refs-real-git.test.ts',
'src/main/git/worktree-safety-real-git.test.ts',
'src/main/ipc/filesystem-quick-open-options.integration.test.ts',
'src/main/ipc/worktree-push-target-cleanup.test.ts',
'src/main/ipc/worktree-push-target-reconciliation.test.ts',
'src/main/ipc/worktrees-ssh-fork-push-target-remote.test.ts',
'src/main/plugins/plugin-host-conformance.test.ts',
'src/main/providers/ssh-directory-listing-options.test.ts',
'src/main/providers/ssh-review-draft-context.test.ts',
'src/main/providers/terminal-path-existence-batch.integration.test.ts',
'src/main/runtime/runtime-file-path-existence.test.ts',
'src/main/runtime/runtime-repository-ref-queries.test.ts',
'src/main/runtime/tui-idle-name-only-real-pty.integration.test.ts',
'src/main/shell-wrapper-generated-file-snapshot.test.ts',
'src/main/ssh/ssh-remote-cli-host-passthrough.test.ts',
'src/main/zsh-scoped-histfile.live-shell.test.ts',
'src/main/zsh-wrapper-version-mismatch.live-shell.test.ts',
'tests/tools/omp-relay-close-lifecycle.test.mjs'
]
function importsRelay(file, contents) {
// Why: parsing every source file overruns the test timeout on a loaded runner.
if (!contents.includes('relay')) {
return false
}
return collectModuleSpecifiers(file, contents).some((specifier) => {
if (!specifier.startsWith('.')) {
return false
}
const target = path.posix.normalize(path.posix.join(path.posix.dirname(file), specifier))
return target === RELAY_SOURCE || target.startsWith(`${RELAY_SOURCE}/`)
})
}
it('keeps everything outside the SSH relay from importing it', () => {
const importers = globSync(
['src', 'config', 'tests', 'mobile/src', 'mobile/app', 'mobile/scripts'].map(
(directory) => `${directory}/**/*.{ts,tsx,mts,cts,js,mjs,cjs}`
),
{
cwd: ROOT,
exclude: [`${RELAY_SOURCE}/**`, '**/node_modules/**', '**/dist/**', '**/out/**']
}
)
.map((file) => file.replaceAll('\\', '/'))
.filter((file) => importsRelay(file, readFileSync(path.join(ROOT, file), 'utf8')))
.sort()
expect(importers.filter((file) => !isTestOnlySourcePath(file))).toEqual([])
expect(importers).toEqual(RELAY_TEST_IMPORTERS)
})
+1
View File
@@ -21,6 +21,7 @@
"../src/shared/**/*",
"../src/packages/process-host/src/__fixtures__/**/*",
"../src/relay/**/*",
"../src/wsl-guest/**/*",
"../src/types/**/*"
],
"compilerOptions": {
+1
View File
@@ -2,6 +2,7 @@
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
"include": [
"../src/relay/**/*",
"../src/wsl-guest/**/*",
"../src/main/pty/overlay-mirror.ts",
"../src/main/pty/shell-startup-env.ts"
],
@@ -6,7 +6,7 @@ import { createHash, randomBytes } from 'node:crypto'
import { writeFileSync, readFileSync } from 'node:fs'
import { SshChannelMultiplexer } from '../../../src/main/ssh/ssh-channel-multiplexer'
import { readFileViaStream } from '../../../src/main/ssh/ssh-filesystem-stream-reader'
import { RelayDispatcher } from '../../../src/relay/dispatcher'
import { RelayDispatcher } from '../../../src/wsl-guest/dispatcher'
import { RelayStreamRegistry } from '../../../src/relay/fs-stream-registry'
import { readRelayFileStreamMetadata } from '../../../src/relay/fs-handler-file-read'
@@ -9,7 +9,7 @@ import {
STREAM_ACK_WINDOW_CHUNKS,
STREAM_CHUNK_SIZE,
RelayErrorCode
} from '../../../src/relay/protocol'
} from '../../../src/wsl-guest/protocol'
import {
candidate,
report,
@@ -3,7 +3,7 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { RelayAgentHookServer } from '../../relay/agent-hook-server'
import { RelayAgentHookServer } from '../../wsl-guest/agent-hook-server'
import { seedLegacyAgentStatusForTests } from '../../shared/agent-hook-listener/listener-state'
import { seedClaudeSubagentRosterFromSnapshots } from '../../shared/agent-hook-listener/providers/claude-roster-state'
import type { AgentHookEventPayload } from '../../shared/agent-hook-listener/listener-event'
@@ -1,4 +1,4 @@
import type { PluginSources } from '../../relay/plugin-overlay'
import type { PluginSources } from '../../wsl-guest/plugin-overlay'
import { isAgentStatusHooksEnabledForAgent } from '../../shared/agent-status-hooks-setting'
import type { ManagedHookDetectionSettings } from './managed-hook-detection-commands'
@@ -2,7 +2,7 @@ import { readFileSync, mkdtempSync, rmSync } from 'node:fs'
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { RelayAgentHookServer } from '../../relay/agent-hook-server'
import { RelayAgentHookServer } from '../../wsl-guest/agent-hook-server'
import { AgentHookServer, _internals } from './server'
import { createRuntimeAutomationRunTerminalObserver } from '../automations/runtime-terminal-run-observer'
import {
@@ -6,7 +6,7 @@ import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { RelayAgentHookServer } from '../../relay/agent-hook-server'
import { RelayAgentHookServer } from '../../wsl-guest/agent-hook-server'
import { AgentHookServer, _internals } from './server'
import { buildBody, PANE } from './server.test-fixtures'
import { hookAt, loadCapture, type CapturedHook } from './claude-cancel-capture.test-fixture'
@@ -1,7 +1,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { AgentHookServer, _internals } from './server'
import { PANE } from './server.test-fixtures'
import { buildRelayHookEnvelope } from '../../relay/agent-hook-envelope-build'
import { buildRelayHookEnvelope } from '../../wsl-guest/agent-hook-envelope-build'
vi.mock('../telemetry/client', () => ({ track: vi.fn() }))
vi.mock('../telemetry/cohort-classifier', () => ({ getCohortAtEmit: () => ({}) }))
@@ -6,7 +6,7 @@ import type { SshChannelMultiplexer } from '../ssh/ssh-channel-multiplexer'
import { AGENT_HOOK_INSTALL_PLUGINS_METHOD } from '../../shared/agent-hook-relay'
import { selectOpenCodePluginSources } from './opencode-plugin-settings'
import type { ManagedHookDetectionSettings } from './managed-hook-detection-commands'
import type { PluginSources } from '../../relay/plugin-overlay'
import type { PluginSources } from '../../wsl-guest/plugin-overlay'
/** Structural, not the deps type itself, so this stays free of the deps module. */
type GuestPluginInstallDeps = {
+1 -1
View File
@@ -14,7 +14,7 @@ import { getOpenCode2PluginSource, getOpenCodePluginSource } from '../opencode/h
import { getPiAgentStatusExtensionSource } from '../pi/agent-status-extension-source'
import { codexHookService } from '../codex/hook-service'
import type { AgentHookInstallStatus } from '../../shared/agent-hook-types'
import type { PluginSources } from '../../relay/plugin-overlay'
import type { PluginSources } from '../../wsl-guest/plugin-overlay'
import {
isWslDistroRunning,
resolveWslHookRelayBundle,
@@ -9,7 +9,7 @@ import { installWslGuestHooks } from './wsl-hook-fs-adapter'
import { REINSTALL_MIN_INTERVAL_MS, type WslHookRelayManagerDeps } from './wsl-hook-relay-deps'
import type { SshChannelMultiplexer } from '../ssh/ssh-channel-multiplexer'
import { openCodePluginSettingsKey } from './opencode-plugin-settings'
import type { PluginSources } from '../../relay/plugin-overlay'
import type { PluginSources } from '../../wsl-guest/plugin-overlay'
/** Structural slice of WslHookRelayManagerDeps — only what an install pass uses. */
type GuestInstallDeps = {
@@ -7,8 +7,8 @@ import { join } from 'node:path'
import type { ChildProcessWithoutNullStreams } from 'node:child_process'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { RelayDispatcher } from '../../relay/dispatcher'
import { registerWslHookFsHandlers } from '../../relay/wsl-hook-fs-bridge'
import { RelayDispatcher } from '../../wsl-guest/dispatcher'
import { registerWslHookFsHandlers } from '../../wsl-guest/wsl-hook-fs-bridge'
import { SshChannelMultiplexer, type MultiplexerTransport } from '../ssh/ssh-channel-multiplexer'
import { createWslHookSftpAdapter } from './wsl-hook-fs-adapter'
import {
@@ -3,9 +3,9 @@ import { mkdirSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import type { TuiAgent } from '../../shared/tui-agent'
import { RelayDispatcher } from '../../relay/dispatcher'
import { PluginOverlayManager } from '../../relay/plugin-overlay'
import { createInstallPluginsHandler } from '../../relay/wsl-install-plugins-handler'
import { RelayDispatcher } from '../../wsl-guest/dispatcher'
import { PluginOverlayManager } from '../../wsl-guest/plugin-overlay'
import { createInstallPluginsHandler } from '../../wsl-guest/wsl-install-plugins-handler'
import { SshChannelMultiplexer } from '../ssh/ssh-channel-multiplexer'
import { AGENT_HOOK_INSTALL_PLUGINS_METHOD } from '../../shared/agent-hook-relay'
import {
@@ -4,9 +4,9 @@ import type { ChildProcessWithoutNullStreams } from 'node:child_process'
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { RelayDispatcher } from '../../relay/dispatcher'
import { PluginOverlayManager } from '../../relay/plugin-overlay'
import { createInstallPluginsHandler } from '../../relay/wsl-install-plugins-handler'
import { RelayDispatcher } from '../../wsl-guest/dispatcher'
import { PluginOverlayManager } from '../../wsl-guest/plugin-overlay'
import { createInstallPluginsHandler } from '../../wsl-guest/wsl-install-plugins-handler'
import { WslHookRelayManager } from './wsl-hook-relay-manager'
import { REINSTALL_MIN_INTERVAL_MS } from './wsl-hook-relay-deps'
import type { MultiplexerTransport } from '../ssh/ssh-channel-multiplexer'
@@ -3,9 +3,9 @@ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import type { TuiAgent } from '../../shared/tui-agent'
import { RelayDispatcher } from '../../relay/dispatcher'
import { PluginOverlayManager } from '../../relay/plugin-overlay'
import { createInstallPluginsHandler } from '../../relay/wsl-install-plugins-handler'
import { RelayDispatcher } from '../../wsl-guest/dispatcher'
import { PluginOverlayManager } from '../../wsl-guest/plugin-overlay'
import { createInstallPluginsHandler } from '../../wsl-guest/wsl-install-plugins-handler'
import { SshChannelMultiplexer } from '../ssh/ssh-channel-multiplexer'
import { AGENT_HOOK_INSTALL_PLUGINS_METHOD } from '../../shared/agent-hook-relay'
import { requestGuestOpenCodeOverlayDir } from './wsl-guest-plugin-install'
@@ -1,7 +1,7 @@
import '../runtime/rpc/unused-default-rpc-methods.test-fixture'
import { afterEach, describe, expect, it } from 'vitest'
import { AiVaultHandler } from '../../relay/ai-vault-handler'
import type { RelayDispatcher } from '../../relay/dispatcher'
import type { RelayDispatcher } from '../../wsl-guest/dispatcher'
import { createSessionSearchClient } from '../../shared/ai-vault-search-client'
import { fakeSearchService } from '../../shared/ai-vault-search-test-fixture'
import { AI_VAULT_AGENTS } from '../../shared/ai-vault-types'
@@ -17,7 +17,7 @@ import {
*
* A failed read is answered as "no hook", which is a known limitation rather than a judgement: a
* missing `orca.yaml` is indistinguishable from an unreachable one here, because the relay rewrites
* a non-numeric error code to `-32000` (`src/relay/dispatcher-rpc-routing.ts`), so nothing survives
* a non-numeric error code to `-32000` (`src/wsl-guest/dispatcher-rpc-routing.ts`), so nothing survives
* to tell ENOENT from a transport failure. Reporting it as unreadable fired on every SSH repo that
* simply has no orca.yaml; blocking on it would refuse those deletes outright. Distinguishing the
* two needs a provider contract that reports absence as a successful outcome — tracked in #20196.
@@ -52,7 +52,7 @@ export async function getArchiveHooksForRemoval(
return getEffectiveHooksFromConfig(repo, yamlHooks)
} catch (error) {
// Indistinguishable from "there is no orca.yaml": the relay rewrites a non-numeric error code
// to -32000 (src/relay/dispatcher-rpc-routing.ts), so nothing survives to tell ENOENT from a
// to -32000 (src/wsl-guest/dispatcher-rpc-routing.ts), so nothing survives to tell ENOENT from a
// transport failure. Logged so an operator can see it; not surfaced, because reporting it as
// unreadable fired on every SSH repo that simply has none. Distinguishing them needs a provider
// contract that returns absence as a successful outcome — #20196.
@@ -6,7 +6,7 @@ import {
createPluginPanelCallAdmission
} from '../../shared/plugins/plugin-panel-call-admission'
import type { PluginPanelActionOutcome } from '../../shared/plugins/plugin-panel-bridge'
import type { MethodHandler } from '../../relay/dispatcher'
import type { MethodHandler } from '../../wsl-guest/dispatcher'
import {
RELAY_PLUGIN_PANEL_HOST_CALL_METHOD,
RELAY_PLUGIN_WORKER_HOST_CALL_METHOD,
@@ -4,7 +4,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
import { runProcess } from '@orca/process-host'
import { ReviewDraftContextError } from '../../shared/review-draft-context-error'
import { RelayContext } from '../../relay/context'
import { RelayDispatcher } from '../../relay/dispatcher'
import { RelayDispatcher } from '../../wsl-guest/dispatcher'
import { GitHandler } from '../../relay/git-handler'
import { createGitTempDir, removeGitTempDir } from '../../relay/git-handler-test-harness'
import { gitCommit, gitInit } from '../../relay/git-handler-test-setup'
@@ -13,7 +13,7 @@ import {
FrameDecoder,
MessageType,
parseJsonRpcMessage
} from '../../relay/protocol'
} from '../../wsl-guest/protocol'
import type { SshChannelMultiplexer } from '../ssh/ssh-channel-multiplexer'
import { getPullRequestDraftContext } from '../text-generation/pull-request-context'
import { SshGitProvider } from './ssh-git-provider'
@@ -5,7 +5,7 @@ import type { SshPortForwardManager } from './ssh-port-forward'
import type { SshConnection } from './ssh-connection'
import type { MultiplexerTransport } from './ssh-channel-multiplexer'
import type { AgentHookRelayEnvelope } from '../../shared/agent-hook-relay'
import { RelayDispatcher } from '../../relay/dispatcher'
import { RelayDispatcher } from '../../wsl-guest/dispatcher'
import {
AGENT_HOOK_NOTIFICATION_METHOD,
AGENT_HOOK_REQUEST_REPLAY_METHOD,
+5 -3
View File
@@ -7,7 +7,7 @@ import {
RelayAgentProcessLifetime
} from './relay-agent-process-lifetime'
import { createFakeChild, requestContext } from './agent-exec-handler-test-harness'
import type { MethodHandler, RelayDispatcher } from './dispatcher'
import type { MethodHandler, RelayDispatcher } from '../wsl-guest/dispatcher'
// Why an untyped mock: the fake child stubs only what AgentExecHandler reads from a ChildProcess.
const { spawnMock, loginShellMock } = vi.hoisted(() => ({
@@ -124,8 +124,10 @@ it('kills a child tracked after the fence instead of throwing', async () => {
const lifetime = new RelayAgentProcessLifetime()
void lifetime.dispose()
const child = createFakeChild()
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: track reads only pid, kill and the close/error events the fake emits.
expect(() => lifetime.track(child as unknown as Parameters<typeof lifetime.track>[0])).not.toThrow()
expect(() =>
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: track reads only pid, kill and the close/error events the fake emits.
lifetime.track(child as unknown as Parameters<typeof lifetime.track>[0])
).not.toThrow()
if (process.platform !== 'win32') {
expect(child.kill).toHaveBeenCalledWith('SIGKILL')
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { EventEmitter } from 'node:events'
import { vi } from 'vitest'
import type { MethodHandler, RequestContext } from './dispatcher'
import type { MethodHandler, RequestContext } from '../wsl-guest/dispatcher'
import { AgentExecHandler } from './agent-exec-handler'
export function withPlatform<T>(platform: NodeJS.Platform, fn: () => T): T {
+1 -1
View File
@@ -3,7 +3,7 @@ import { PromiseSettlementWaiters } from '../shared/promise-settlement-waiters'
import { spawn, type ChildProcess } from 'node:child_process'
import { existsSync } from 'node:fs'
import { delimiter, join } from 'node:path'
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import { applyTerminalGitCredentialPromptGuard } from '../shared/terminal-git-credential-guard'
import { mergeGitConfigEnvProtocol } from '../shared/git-credential-prompt-env'
import { terminateRelaySubprocessTree } from './subprocess-tree-termination'
@@ -2,13 +2,13 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { RelayAgentHookServer } from './agent-hook-server'
import { RelayAgentHookServer } from '../wsl-guest/agent-hook-server'
import { RelayAgentHookRuntime } from './relay-agent-hook-runtime'
import { RetiredPaneSurfaceRegistry } from './retired-pane-surfaces'
import type { AgentHookRelayEnvelope } from '../shared/agent-hook-relay'
import { makePaneKey } from '../shared/stable-pane-id'
import type { PtyHandler, PtySurfaceRetiredListener } from './pty-handler'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
import type { ClaudeTerminalEvidence } from '../shared/claude-terminal-interrupt'
const LEAF_ID = '11111111-1111-4111-8111-111111111111'
+1 -1
View File
@@ -11,7 +11,7 @@ import { getRemoteHostPlatform } from '../main/ssh/ssh-remote-platform'
import type { RemoteHostPlatform } from '../main/ssh/ssh-remote-platform'
import { scanRemoteAiVaultSessions } from '../main/ai-vault/remote-session-scanner'
import { readAiVaultSessionTitlesFromFiles } from '../main/ai-vault/session-title-file-reader'
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import { AiVaultHandler } from './ai-vault-handler'
import { createRelayAiVaultFilesystemProvider } from './ai-vault-service-filesystem'
import type { RelayAiVaultServiceApi } from './ai-vault-service-client-state'
+1 -1
View File
@@ -20,7 +20,7 @@ import {
import { getRemoteHostPlatform, type RemoteHostPlatform } from '../main/ssh/ssh-remote-platform'
import { parseUnameToRelayPlatform } from '../main/ssh/relay-protocol'
import { relayLogLine } from './relay-diagnostic-log'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
import { AiVaultScanCoordinator } from '../main/ai-vault/ai-vault-scan-coordinator'
import type { RelayAiVaultServiceApi } from './ai-vault-service-client-state'
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { ExternalAutomationCommandExecutor } from './external-automation-command-executor'
import { ExternalAutomationsHandler } from './external-automations-handler'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
const execFileMock = vi.hoisted(() =>
vi.fn((...args: unknown[]) => {
+1 -1
View File
@@ -1,6 +1,6 @@
import { execFile } from 'node:child_process'
import { promisify } from 'node:util'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
import {
ExternalAutomationCommandExecutor,
type ExternalAutomationCommandRunner
+2 -2
View File
@@ -2,10 +2,10 @@ import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import { readRelayFileStreamMetadata } from './fs-handler-file-read'
import { RelayStreamRegistry } from './fs-stream-registry'
import { STREAM_CHUNK_SIZE } from './protocol'
import { STREAM_CHUNK_SIZE } from '../wsl-guest/protocol'
let directory: string
let filePath: string
@@ -4,8 +4,8 @@ import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { FsHandler } from './fs-handler'
import { RelayContext } from './context'
import { RelayDispatcher } from './dispatcher'
import { encodeJsonRpcFrame, RelayErrorCode } from './protocol'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import { encodeJsonRpcFrame, RelayErrorCode } from '../wsl-guest/protocol'
import { FileRangeReadRequestError, MAX_FILE_RANGE_READ_BYTES } from '../shared/file-range-read'
/** Minimal dispatcher: this suite only needs the registered request handlers.
+2 -2
View File
@@ -9,8 +9,8 @@ import {
MAX_FILE_RANGE_READ_BYTES,
validateFileRangeRequest
} from '../shared/file-range-read'
import { HEADER_LENGTH, prepareJsonRpcPayload } from './protocol'
import { DISPATCHER_CONTROL_QUEUE_MAX_BYTES } from './dispatcher-writer-admission'
import { HEADER_LENGTH, prepareJsonRpcPayload } from '../wsl-guest/protocol'
import { DISPATCHER_CONTROL_QUEUE_MAX_BYTES } from '../wsl-guest/dispatcher-writer-admission'
let roots: string[] = []
+2 -2
View File
@@ -1,8 +1,8 @@
import { open, readFile, stat } from 'node:fs/promises'
import type { FileHandle } from 'node:fs/promises'
import { extname } from 'node:path'
import type { RelayDispatcher, RequestContext } from './dispatcher'
import { STREAM_ACK_WINDOW_CHUNKS, STREAM_CHUNK_SIZE } from './protocol'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import { STREAM_ACK_WINDOW_CHUNKS, STREAM_CHUNK_SIZE } from '../wsl-guest/protocol'
import type { RelayStreamRegistry } from './fs-stream-registry'
import { BINARY_PROBE_BYTES, isBinaryBuffer } from '../shared/binary-buffer'
import {
+1 -1
View File
@@ -19,7 +19,7 @@ import {
expandQuickOpenGitFileListing,
parseQuickOpenGitLsFilesEntry
} from '../shared/quick-open-readdir-walk'
import { buildRelayGitEnv } from './relay-command-env'
import { buildRelayGitEnv } from '../wsl-guest/relay-command-env'
/**
* List files using `git ls-files`. Fallback when rg is not installed.
+1 -1
View File
@@ -18,7 +18,7 @@ import {
absorbPendingRipgrepSpawnError,
killSpawnedRipgrepProcess
} from '../shared/ripgrep-process-availability'
import { buildRelayGitEnv } from './relay-command-env'
import { buildRelayGitEnv } from '../wsl-guest/relay-command-env'
/**
* Text search using `git grep`. Fallback when rg is not installed.
@@ -17,7 +17,7 @@ import { listFilesWithGit } from './fs-handler-git-fallback'
import { listFilesWithRg } from './fs-handler-list-files'
import { searchWithRg } from './fs-handler-utils'
import { RipgrepUnavailableError } from '../shared/ripgrep-process-availability'
import { buildRelayCommandEnv } from './relay-command-env'
import { buildRelayCommandEnv } from '../wsl-guest/relay-command-env'
import { configureRelayBundledRipgrep } from './relay-bundled-ripgrep'
import {
ListFilesScanCoordinator,
@@ -21,7 +21,7 @@ vi.mock('@parcel/watcher', () => ({ subscribe: vi.fn() }))
import { FsHandler } from './fs-handler'
import { RelayContext } from './context'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
import { QUICK_OPEN_LISTING_MAX_RESULTS } from '../shared/quick-open-listing-limits'
type ListFilesHandler = (
+1 -1
View File
@@ -32,7 +32,7 @@ import {
RipgrepUnavailableError
} from '../shared/ripgrep-process-availability'
import { QuickOpenPathRanker } from '../shared/quick-open-path-search'
import { buildRelayCommandEnv } from './relay-command-env'
import { buildRelayCommandEnv } from '../wsl-guest/relay-command-env'
import {
pathRipgrepCommand,
resolveRelayRipgrepCommand,
@@ -46,7 +46,7 @@ import { RipgrepUnavailableError } from '../shared/ripgrep-process-availability'
import { RelayContext } from './context'
import { FsHandler } from './fs-handler'
import { runListFilesScan } from './fs-list-files-fallback-chain'
import { buildRelayCommandEnv } from './relay-command-env'
import { buildRelayCommandEnv } from '../wsl-guest/relay-command-env'
type FsHandlerInternals = {
search(params: Record<string, unknown>): Promise<unknown>
+2 -2
View File
@@ -1,8 +1,8 @@
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
import { FsHandler } from './fs-handler'
import { RelayContext } from './context'
import type { RelayDispatcher } from './dispatcher'
import { MAX_CONCURRENT_STREAMS, STREAM_CHUNK_SIZE } from './protocol'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
import { MAX_CONCURRENT_STREAMS, STREAM_CHUNK_SIZE } from '../wsl-guest/protocol'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { mkdtempSync, writeFileSync } from 'node:fs'
+1 -1
View File
@@ -28,7 +28,7 @@ import {
ripgrepMissingCwdError,
RipgrepUnavailableError
} from '../shared/ripgrep-process-availability'
import { buildRelayCommandEnv } from './relay-command-env'
import { buildRelayCommandEnv } from '../wsl-guest/relay-command-env'
import {
pathRipgrepCommand,
resolveRelayRipgrepCommand,
+1 -1
View File
@@ -2,7 +2,7 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
import { FsHandler } from './fs-handler'
import { MAX_TEXT_FILE_SIZE } from './fs-handler-utils'
import { RelayContext } from './context'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import { mkdtempSync, writeFileSync, mkdirSync, symlinkSync } from 'node:fs'
+1 -1
View File
@@ -6,7 +6,7 @@ import { quickOpenRecentCandidateSet } from '../shared/quick-open-recent-candida
import { QUICK_OPEN_SEARCH_VERSION } from '../shared/quick-open-path-search'
import { pathsExistOnRelay } from './fs-path-existence'
import { tmpdir } from 'node:os'
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import type { RelayContext } from './context'
// Why: RelayContext is accepted in the constructor for protocol back-compat
// (see docs/relay-fs-allowlist-removal.md), but no longer consulted on FS ops.
@@ -62,7 +62,7 @@ import {
SshChannelMultiplexer,
type MultiplexerTransport
} from '../main/ssh/ssh-channel-multiplexer'
import { RelayDispatcher } from './dispatcher'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import { RelayContext } from './context'
import { FsHandler } from './fs-handler'
import { LIST_FILES_SUPERSEDED_MESSAGE } from './fs-list-files-scan-coordinator'
@@ -1,6 +1,6 @@
import { SshChannelMultiplexer } from '../main/ssh/ssh-channel-multiplexer'
import { RelayContext } from './context'
import { RelayDispatcher } from './dispatcher'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import { FsHandler } from './fs-handler'
export function createRelayFileListingRequestHarness() {
+1 -1
View File
@@ -8,7 +8,7 @@ import {
} from '../shared/file-listing-cancellation'
import { isQuickOpenReaddirBudgetError } from '../shared/quick-open-readdir-walk'
import { buildInstallRgMessage, buildRipgrepRequiredMessage } from '../shared/quick-open-install-rg'
import { buildRelayCommandEnv } from './relay-command-env'
import { buildRelayCommandEnv } from '../wsl-guest/relay-command-env'
import { RipgrepUnavailableError } from '../shared/ripgrep-process-availability'
import { QuickOpenPathRanker } from '../shared/quick-open-path-search'
@@ -21,8 +21,8 @@ import {
} from '../main/ssh/ssh-channel-multiplexer'
import { requestGitStreamable } from '../main/ssh/ssh-git-response-stream-reader'
import { RelayContext } from './context'
import { RelayDispatcher } from './dispatcher'
import { DISPATCHER_CONTROL_QUEUE_MAX_BYTES } from './dispatcher-writer-admission'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import { DISPATCHER_CONTROL_QUEUE_MAX_BYTES } from '../wsl-guest/dispatcher-writer-admission'
import { FsHandler } from './fs-handler'
import { GitHandler } from './git-handler'
import { GitResponseStreamRegistry } from './git-response-stream'
@@ -9,7 +9,7 @@ import { listFilesWithRg } from './fs-handler-list-files'
import { listRelayMarkdownDocuments } from './fs-markdown-document-listing'
import { RelayContext } from './context'
import { FsHandler } from './fs-handler'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
it.skipIf(process.platform === 'win32')(
'keeps readable SSH Markdown documents when a child directory is unreadable',
+2 -2
View File
@@ -29,10 +29,10 @@ vi.mock('./relay-bundled-ripgrep', () => ({
import { searchWithRg } from './fs-handler-utils'
import { searchWithGitGrep } from './fs-handler-git-fallback'
import { RelayDispatcher } from './dispatcher'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import { FsHandler } from './fs-handler'
import { RelayContext } from './context'
import { encodeJsonRpcFrame } from './protocol'
import { encodeJsonRpcFrame } from '../wsl-guest/protocol'
function createProcess(spawned = true) {
const child = new ChildProcess()
@@ -27,11 +27,11 @@ import {
} from '../main/ssh/ssh-channel-multiplexer'
import { readFileViaStream } from '../main/ssh/ssh-filesystem-stream-reader'
import { RelayDispatcher } from './dispatcher'
import type { SinkWriteSettlement } from './dispatcher'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import type { SinkWriteSettlement } from '../wsl-guest/dispatcher'
import { RelayContext } from './context'
import { FsHandler } from './fs-handler'
import { STREAM_CHUNK_SIZE } from './protocol'
import { STREAM_CHUNK_SIZE } from '../wsl-guest/protocol'
// One framed fs.streamChunk: 256KB raw → base64 (4/3) + JSON envelope + header.
const FRAMED_CHUNK_BYTES = Math.ceil((STREAM_CHUNK_SIZE * 4) / 3) + 512
+1 -1
View File
@@ -1,6 +1,6 @@
import type { FileHandle } from 'node:fs/promises'
import { settlesWithin } from './settles-within'
import { MAX_CONCURRENT_STREAMS, RelayErrorCode } from './protocol'
import { MAX_CONCURRENT_STREAMS, RelayErrorCode } from '../wsl-guest/protocol'
import { RelayStreamAckWindow } from './relay-stream-ack-window'
type StreamEntry = { handle: FileHandle; ack: RelayStreamAckWindow }
@@ -1,4 +1,4 @@
import type { RequestContext } from './dispatcher'
import type { RequestContext } from '../wsl-guest/dispatcher'
import { GitHandlerOperationContext } from './git-handler-operation-context'
import { branchCompare as branchCompareOp } from './git-handler-ops'
import { commitCompare as commitCompareOp } from './git-handler-commit-diff-ops'
+1 -1
View File
@@ -1,4 +1,4 @@
import type { RequestContext } from './dispatcher'
import type { RequestContext } from '../wsl-guest/dispatcher'
import { GitHandlerOperationContext } from './git-handler-operation-context'
import { validateGitExecArgs } from './git-handler-ops'
import { gitExecMutatesRepository } from '../shared/git-exec-mutation'
+1 -1
View File
@@ -1,4 +1,4 @@
import type { RequestContext } from './dispatcher'
import type { RequestContext } from '../wsl-guest/dispatcher'
import { GitHandlerOperationContext } from './git-handler-operation-context'
import { assertValidGitPushTarget } from '../shared/git-push-target-validation'
import type { GitPushTarget } from '../shared/worktree/types'
@@ -1,4 +1,4 @@
import type { RequestContext } from './dispatcher'
import type { RequestContext } from '../wsl-guest/dispatcher'
import { GitHandlerOperationContext } from './git-handler-operation-context'
import { branchDiffEntries } from './git-handler-ops'
import {
+1 -1
View File
@@ -1,4 +1,4 @@
import type { RequestContext } from './dispatcher'
import type { RequestContext } from '../wsl-guest/dispatcher'
import type { GitStatusReadLeaseOwner } from '../shared/git-status-read-lease-owner'
import type { GitCapabilityCache } from '../shared/git-capability-cache'
import type { SubmodulePathsCache } from './git-handler-submodule-ops'
+1 -1
View File
@@ -1,5 +1,5 @@
import * as path from 'node:path'
import type { RequestContext } from './dispatcher'
import type { RequestContext } from '../wsl-guest/dispatcher'
import { GitHandlerOperationContext } from './git-handler-operation-context'
import { getStatusOp } from './git-handler-status-ops'
import { streamRelayGitStdout } from './git-stdout-stream'
+1 -1
View File
@@ -1,4 +1,4 @@
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import type { GitHandlerOperationSet } from './git-handler-operation-set'
export function registerGitHandlers(
@@ -7,8 +7,8 @@ import { execFileSync } from 'node:child_process'
import { GitHandler } from './git-handler'
import { RelayContext } from './context'
import { DEFAULT_GIT_STATUS_LIMIT } from '../shared/git-status-limit'
import { RelayDispatcher } from './dispatcher'
import type { MethodHandler } from './dispatcher-contract'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import type { MethodHandler } from '../wsl-guest/dispatcher-contract'
import { gitCommit, gitInit } from './git-handler-test-setup'
const OVER_CAP = DEFAULT_GIT_STATUS_LIMIT + 50
+1 -1
View File
@@ -1,5 +1,5 @@
import { randomUUID } from 'node:crypto'
import type { RequestContext } from './dispatcher'
import type { RequestContext } from '../wsl-guest/dispatcher'
import { GitHandlerOperationContext } from './git-handler-operation-context'
import { resolveRelayPushTarget } from './git-handler-push-target'
import { normalizeGitErrorMessage, runPullWithDivergenceFallback } from '../shared/git-remote-error'
+1 -1
View File
@@ -7,7 +7,7 @@
*/
import { vi } from 'vitest'
import { execFileSync } from 'node:child_process'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
const TEST_GIT_USER_EMAIL = 'test@test.com'
const TEST_GIT_USER_NAME = 'Test'
+1 -1
View File
@@ -1,7 +1,7 @@
import { throwIfSignalAborted } from '../shared/abort-signal-reason'
import { annotateWorktreeLocksFromAdmin } from '../shared/git-worktree-admin'
import * as path from 'node:path'
import type { RequestContext } from './dispatcher'
import type { RequestContext } from '../wsl-guest/dispatcher'
import { expandTilde } from './context'
import { GitHandlerOperationContext } from './git-handler-operation-context'
import { isUnsupportedWorktreeListZError } from './git-handler-utils'
+2 -2
View File
@@ -1,4 +1,4 @@
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import type { RelayContext } from './context'
import { expandTilde } from './context'
import { MAX_IN_FLIGHT_PROMISE_DEDUPE_ENTRIES } from '../shared/in-flight-promise-dedupe'
@@ -12,7 +12,7 @@ import {
import { GitResponseStreamRegistry, maybeStreamRpcResponse } from './git-response-stream'
import { clearGitStatusLineStatsCache } from '../shared/git-status-line-stats-cache'
import { invalidateGitBranchLineTotalInFlight } from '../shared/git-branch-line-total'
import { buildRelayGitEnv, buildRelayUnattendedGitEnv } from './relay-command-env'
import { buildRelayGitEnv, buildRelayUnattendedGitEnv } from '../wsl-guest/relay-command-env'
import { getGitCloneFailureMessage } from '../shared/git-clone-failure-message'
import type {
GitHandlerCommandOptions,
@@ -27,11 +27,11 @@ import {
} from '../main/ssh/ssh-channel-multiplexer'
import { requestGitStreamable } from '../main/ssh/ssh-git-response-stream-reader'
import { RelayDispatcher } from './dispatcher'
import type { SinkWriteSettlement } from './dispatcher'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import type { SinkWriteSettlement } from '../wsl-guest/dispatcher'
import { RelayContext } from './context'
import { GitHandler } from './git-handler'
import { GIT_RESPONSE_CHUNK_SIZE } from './protocol'
import { GIT_RESPONSE_CHUNK_SIZE } from '../wsl-guest/protocol'
// One framed git.responseChunk: base64 (4/3) + JSON envelope + header slack.
const FRAMED_CHUNK_BYTES = Math.ceil((GIT_RESPONSE_CHUNK_SIZE * 4) / 3) + 512
@@ -1,7 +1,7 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import { GitResponseStreamRegistry } from './git-response-stream'
import { GIT_RESPONSE_CHUNK_SIZE, STREAM_ACK_WINDOW_CHUNKS } from './protocol'
import { GIT_RESPONSE_CHUNK_SIZE, STREAM_ACK_WINDOW_CHUNKS } from '../wsl-guest/protocol'
async function flushPump(): Promise<void> {
await new Promise<void>((resolve) => setImmediate(resolve))
@@ -1,6 +1,6 @@
import { createHash } from 'node:crypto'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { RelayDispatcher } from './dispatcher'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import { GitResponseStreamRegistry } from './git-response-stream'
async function collect(): Promise<void> {
@@ -1,7 +1,7 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import { GitResponseStreamRegistry } from './git-response-stream'
import { GIT_RESPONSE_CHUNK_SIZE, STREAM_ACK_WINDOW_CHUNKS } from './protocol'
import { GIT_RESPONSE_CHUNK_SIZE, STREAM_ACK_WINDOW_CHUNKS } from '../wsl-guest/protocol'
const context: RequestContext = { clientId: 7, isStale: () => false }
+2 -2
View File
@@ -13,13 +13,13 @@
// `git.responseAck` can credit the ack window a pump parks on — the other's
// streams would stall at STREAM_ACK_WINDOW_CHUNKS forever. See
// `relay-runtime-services.ts` for the wiring.
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import {
GIT_RESPONSE_CHUNK_SIZE,
GIT_RESPONSE_STREAM_THRESHOLD,
STREAM_ACK_WINDOW_CHUNKS,
type GitResponseStreamMarker
} from './protocol'
} from '../wsl-guest/protocol'
import { settlesWithin } from './settles-within'
import { RelayStreamAckWindow } from './relay-stream-ack-window'
import { errorMessage } from '../shared/error-message'
+1 -1
View File
@@ -4,7 +4,7 @@ import { forceTerminateProcessTree } from '@orca/process-host/process-tree-termi
import { createChildTerminationReporter } from '@orca/process-host/child-termination-reporter'
import { GitCommandTimeoutError, gitCommandTimeoutMs } from '../shared/git-command-timeout'
import { expandTilde } from './context'
import { buildRelayGitEnv } from './relay-command-env'
import { buildRelayGitEnv } from '../wsl-guest/relay-command-env'
import { acquireRelayGitAdmission } from './git-handler-command-termination'
const DEFAULT_RELAY_GIT_STREAM_MAX_BYTES = 10 * 1024 * 1024
+1 -1
View File
@@ -19,7 +19,7 @@ import {
type MultiplexerTransport
} from '../main/ssh/ssh-channel-multiplexer'
import { RelayDispatcher } from './dispatcher'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import { RelayContext } from './context'
import { FsHandler } from './fs-handler'
import { GitHandler } from './git-handler'
+1 -1
View File
@@ -1,6 +1,6 @@
import { describe, expect, it, vi } from 'vitest'
import { AGENT_HOOK_INSTALL_MANAGED_HOOKS_METHOD } from '../shared/agent-hook-relay'
import type { MethodHandler, RequestContext } from './dispatcher'
import type { MethodHandler, RequestContext } from '../wsl-guest/dispatcher'
import { registerManagedHookInstaller, type ManagedHookRuntime } from './managed-hook-installer'
function captureHandler(loadRuntime: () => ManagedHookRuntime): MethodHandler {
+1 -1
View File
@@ -3,7 +3,7 @@ import {
AGENT_HOOK_INSTALL_MANAGED_HOOKS_METHOD,
type AgentHookInstallManagedHooksParams
} from '../shared/agent-hook-relay'
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import type { AgentHookTarget } from '../shared/agent-hook-types'
import { isManagedAgentHookTarget } from '../shared/managed-agent-hook-targets'
import { parseClaudeCliVersion } from '../main/claude/claude-hook-event-versions'
@@ -5,10 +5,10 @@ import { tmpdir } from 'node:os'
import type * as NodeOs from 'node:os'
import { delimiter, join } from 'node:path'
import { afterEach, expect, it, vi } from 'vitest'
import { RelayDispatcher } from './dispatcher'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import { PtyHandler } from './pty-handler'
import { RelayAgentHookRuntime } from './relay-agent-hook-runtime'
import { PluginOverlayManager } from './plugin-overlay'
import { PluginOverlayManager } from '../wsl-guest/plugin-overlay'
import { withFreshOmpLaunch } from '../shared/omp-fresh-launch'
const state = vi.hoisted(() => ({ home: '' }))
+2 -2
View File
@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { beginPtyHandlerTest, endPtyHandlerTest } from './pty-handler-test-harness'
import { RelayAgentHookRuntime } from './relay-agent-hook-runtime'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
import { AGENT_HOOK_INSTALL_PLUGINS_METHOD } from '../shared/agent-hook-relay'
const mocks = vi.hoisted(() => ({
@@ -33,7 +33,7 @@ vi.mock('../main/opencode/opencode-launch-capabilities', () => ({
vi.mock('../main/pty/posix-pty-process-groups', () => ({
forceKillPosixPtyProcessGroups: vi.fn((_pid: number, fallback: () => void) => fallback())
}))
vi.mock('./agent-hook-server', () => ({
vi.mock('../wsl-guest/agent-hook-server', () => ({
RelayAgentHookServer: class {
start = async () => {}
stop = () => {}
+1 -1
View File
@@ -1,4 +1,4 @@
import type { MethodHandler, RequestContext } from './dispatcher'
import type { MethodHandler, RequestContext } from '../wsl-guest/dispatcher'
import {
admitPluginPanelCall,
createPluginPanelCallAdmission,
+1 -1
View File
@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { MethodHandler, RequestContext } from './dispatcher'
import type { MethodHandler, RequestContext } from '../wsl-guest/dispatcher'
const { readFileMock, readdirMock, readlinkMock } = vi.hoisted(() => ({
readFileMock: vi.fn(),
+1 -1
View File
@@ -1,6 +1,6 @@
import { readFile, readdir, readlink } from 'node:fs/promises'
import { getProcessOutputFields } from '../shared/process-output-field-scanner'
import type { RelayDispatcher, RequestContext } from './dispatcher'
import type { RelayDispatcher, RequestContext } from '../wsl-guest/dispatcher'
import { scanWindowsListeningPorts } from './windows-port-scan'
// Keep in sync with src/shared/ssh-types.ts — DetectedPort
@@ -19,7 +19,7 @@ const { mockPtySpawn, mockPtyInstance } = vi.hoisted(() => ({
vi.mock('node-pty', () => ({ spawn: mockPtySpawn }))
import { PtyHandler } from './pty-handler'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
// Mirrors the relay drain constants; kept local so a constant change fails this oracle loudly.
const CHUNK_CHARS = 16 * 1024
@@ -37,8 +37,8 @@ vi.mock('../main/shell-prompt-readiness-probe', () => ({
}))
import { PtyHandler } from './pty-handler'
import { RelayDispatcher } from './dispatcher'
import { encodeJsonRpcFrame } from './protocol'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import { encodeJsonRpcFrame } from '../wsl-guest/protocol'
import type { RelayPtySourcePublication } from './relay-pty-source-publication'
import {
beginPtyHandlerTest,
@@ -2,8 +2,12 @@ import './mock-descendant-sweep'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type * as CryptoModule from 'node:crypto'
import type * as SnapshotModule from '../shared/process-table-snapshot-reader'
import { RelayDispatcher } from './dispatcher'
import { encodeJsonRpcFrame, parseJsonRpcMessage, type JsonRpcResponse } from './protocol'
import { RelayDispatcher } from '../wsl-guest/dispatcher'
import {
encodeJsonRpcFrame,
parseJsonRpcMessage,
type JsonRpcResponse
} from '../wsl-guest/protocol'
import { PtyHandler } from './pty-handler'
import { ClaimedAgentPtyOwnerRegistry } from '../shared/claimed-agent-pty-owner'
import {
+2 -2
View File
@@ -1,8 +1,8 @@
import './mock-descendant-sweep'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { PROCESS_BOUNDARY_GROUND } from '../shared/terminal-mode-reset-profiles'
import { RelayDispatcher, type RelayClientSessionIdentity } from './dispatcher'
import { encodeJsonRpcFrame, MessageType } from './protocol'
import { RelayDispatcher, type RelayClientSessionIdentity } from '../wsl-guest/dispatcher'
import { encodeJsonRpcFrame, MessageType } from '../wsl-guest/protocol'
import { PtyHandler } from './pty-handler'
import { TEST_PTY_ID_MINT_EPOCH } from './pty-handler-test-harness'
import { RelayPtySourcePublication } from './relay-pty-source-publication'
@@ -5,8 +5,8 @@ import {
RelayDispatcher,
type RelayClientSessionIdentity,
type SinkWriteSettlement
} from './dispatcher'
import { encodeJsonRpcFrame, MessageType } from './protocol'
} from '../wsl-guest/dispatcher'
import { encodeJsonRpcFrame, MessageType } from '../wsl-guest/protocol'
import { PtyHandler } from './pty-handler'
import { TEST_PTY_ID_MINT_EPOCH, testPtyId } from './pty-handler-test-harness'
import { RelayPtySourcePublication } from './relay-pty-source-publication'
@@ -41,7 +41,7 @@ vi.mock('../main/shell-prompt-readiness-probe', () => ({
}))
import { MAX_RELAY_PTY_SESSIONS, PtyHandler } from './pty-handler'
import type { RelayDispatcher } from './dispatcher'
import type { RelayDispatcher } from '../wsl-guest/dispatcher'
import {
beginPtyHandlerTest,
createMockDispatcher,

Some files were not shown because too many files have changed in this diff Show More