refactor(runtime): collapse speculative portability layers

Remove the one-consumer bounded-read primitive and shared terminal lifecycle controller, make terminal cleanup one awaited provider operation, and reuse one Code Runtime contract suite. Keep only reproduced cancellation and policy fixes; defer unproven replacement, prompt-attribution, and streaming-frame concerns to scoped markers.
This commit is contained in:
Tianyi Cui
2026-07-29 18:26:21 +08:00
parent 4fecc54998
commit c1d550de58
65 changed files with 1265 additions and 948 deletions

View File

@@ -11,8 +11,7 @@ The subprocess seam (`ctx.subprocess`) is the process half of one execution worl
- The spec is fully explicit — argv, cwd, per-stream stdio dispositions, grace — because deployment-varying defaults belong to the calling seam's config, not to a hidden subprocess-service default (the `dsh-bash` request/spec split is the owning template). `argv` is never shell-interpreted; a consumer that wants a shell passes `['bash', '-c', command]` itself.
- Stdio is Node-shaped per stream: `'pipe'` hands the caller the raw stream for its own protocol framing (LSP JSON-RPC, ACP ndjson), `'inherit'` passes the parent descriptor through for diagnostics, and collect mode (`{ maxBytes, spill? }`) buffers a bounded tail with an optional full-stream spill file. Collect readers take whole-stream byte offsets and never consume, so independent readers cannot steal one another's deltas; a read whose offset slid out of the in-memory tail is `lossy` and points at the spill file when one exists. Collected output stays readable after settlement.
- Termination is tree-scoped on every platform (POSIX detached groups with direct-child fallback; Windows `taskkill /T`): `terminate()` — the only termination verb — escalates SIGTERM→grace→SIGKILL (idempotent, driven by the spec's abort signal too, a no-op once the tree is gone), and `waitForExit(signal?)` observes whole-tree liveness so a consumer-owned teardown ladder holds each tier on real quiescence — the manager reacts but never classifies why (callers own deadlines, teardown ladders, and cause classification).
- `spawnTerminal(spec)` is the only non-pipe primitive. Its handle owns a real PTY, valid-UTF-8 byte I/O, foreground-process-group inspection/signalling, TERM-to-KILL whole-session cleanup, and a quiescence wait. The output stream ends after queued output when the top-level process exits; a live transport failure rejects `done`. These operations remain one substrate primitive because ordinary pipes cannot allocate a controlling terminal or prove and clean the complete terminal session; readiness, scrollback, and owner policy remain in the PTY consumer.
- `SubprocessTerminalLifecycle` composes a handle's top-level `done` promise with its provider-owned session cleanup. It binds lifetime cancellation, shares one active cleanup attempt, permits a failed attempt to retry, normalizes cleanup rejections, and bounds quiescence observation without knowing the provider's process mechanics.
- `spawnTerminal(spec)` is the only non-pipe primitive. Its handle owns a real PTY, UTF-8 text I/O, foreground-process-group inspection/signalling, and one awaited `terminate()` operation that reaches whole-session quiescence and settles in-flight handle calls. The spec signal cancels allocation only; the published handle owns its lifetime. The output stream ends after queued output when the top-level process exits, and a live transport failure rejects `done`. These operations remain one substrate primitive because ordinary pipes cannot allocate a controlling terminal or clean the complete terminal session; readiness, scrollback, and owner policy remain in the PTY consumer.
- `scrubbedParentEnv()` / `SENSITIVE_ENV_PATTERN` are the one shared scrub definition: ambient credential-shaped and `DSH_*` names are dropped, and explicit `env` merges after the scrub. The local ordinary and terminal spawns both apply it; SDK-managed transports that own their spawn may import it directly.
- Disposal of the service terminates all still-running managed processes and awaits their exit.