feat(host): capture HostEnvironment once at entry points

TH
15m ago
+5236-1803

Summary

Part of #3747. Closes #3749.

This branch is the whole HostEnvironment adoption: #3775 (root src/ and the remaining packages) and #3777 (platform and capture packages) are merged into it, on top of this PR's own foundation.

HostEnvironment = { env, cwd } now has one owner. Entry points capture it once, and the daemon runtime, request scope and command spawning receive it instead of reading process. About 260 files change, most of them test call sites.

  • Type and capture: HostEnvironment is in @agent-device/kernel/source-value. captureHostEnvironment(process) is in @agent-device/host-kit/process. It copies the environment.
  • Entry points: src/bin.ts, src/cli.ts (runCli(argv, deps, host)), src/daemon.ts and createAgentDeviceClient(config, { host }) capture once. A client built without a host captures process and then reads only that host: its default transport, daemon launch, state dir and stateDir() all resolve from it.
  • Daemon: startDaemonRuntime requires host. DaemonRuntimeDepsInput.host replaces env. RequestRouterDeps.host and RequestExecutionScope.host carry it. Request handlers, claim writers, lease admission and the reapers in packages/daemon read configuration from that host explicitly (through RequestHandlerChainParams.host or the env their constructor receives), not from the ambient scope.
  • Command spawning: withCommandHost({ host, requestEnv }, fn) replaces withRequestCommandEnv. Spawned commands start from host.env. commandHostEnvironment() still gives platform and root code that host and throws outside one; it is meant for command execution only, and their owners (#3750, #3751) convert the remaining readers to explicit hosts.
  • Explicit inputs: whichCmd, resolveUserPath, expandSessionPath, the device-claim functions, loadDaemonPolicy, launchDaemonProcess and kernel allowlist and simulator-set resolvers take env or cwd as parameters.
  • Done when: host-kit, kernel and daemon-contracts non-test sources have no process.env or process.cwd().

Behavior changes: a relative state dir without a cwd is refused (relative_state_dir_without_cwd; callers now pass one), and a relative AGENT_DEVICE_CLAIMS_DIR is refused (relative_claims_dir; the store is shared by every daemon on the host, so it used to depend on where one of them started). Suites that reach host-reading code outside a daemon request or the CLI bind a host with bindTestCommandHost() from test-kit; there is no global test setup.

The root src/ readers (#3775) and the platform and capture packages (#3777) now read host settings from HostEnvironment, merged into this branch. commandHostEnvironment() remains the ambient host for command execution and for a number of platform-side readers that still call it; narrowing it further is a follow-up.

Validation

pnpm check:affected --run --head fab82cc123 (exactCommit) passed, with current main, #3775 and #3777 merged.

Behavior note (PNG worker): packages/capture-kit/src/png-worker-client.ts no longer calls process.emitWarning when the worker is unavailable and starts with env {}; the reason is reported once through the png_worker_unavailable diagnostic (also in CHANGELOG).

🤖 Generated with Claude Code