Skip to content

feat(host): capture HostEnvironment once at entry points - #3763

Open
thymikee wants to merge 34 commits into
mainfrom
refactor/host-environment
Open

thymikee wants to merge 34 commits into
mainfrom
refactor/host-environment

Conversation

@thymikee

@thymikee thymikee commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Summary

Part of #3747. Closes #3749.

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 (refactor(platforms): read host settings from HostEnvironment in platform and capture packages #3750, refactor: read host settings from HostEnvironment in root src and remaining packages #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 moved to the host in #3775, which merged into this branch. The platform and capture packages still read the process directly, for #3777 to migrate, so commandHostEnvironment() stays until it lands.

Validation

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

🤖 Generated with Claude Code

thymikee and others added 3 commits October 16, 2026 22:03
Add HostEnvironment to the kernel and captureHostEnvironment to host-kit.
The CLI, the daemon executable and the client factory capture it once;
startDaemonRuntime requires it and the request scope exposes it. Host-kit,
kernel and daemon-contracts no longer read the process environment or
working directory.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…irectory

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Platform packages may not read process; they take the environment and
working directory of the enclosing command host. Unit tests enter the live
process environment as their host.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.38 MB 5.38 MB +3.8 kB
Package (unpacked) 5.38 MB 5.38 MB +3.8 kB
Package (download) 1.62 MB 1.62 MB +1.9 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.4 ms 43.6 ms +16.1 ms
CLI --help 78.6 ms 81.9 ms +3.2 ms

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 178 files

You’re at about 91% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread src/cli/commands/connection-runtime.ts Outdated
Comment thread packages/daemon/src/sessions/handlers/session-boot.ts Outdated
Comment thread packages/daemon/src/server/http/daemon-shutdown-claims.ts Outdated
Comment thread packages/daemon/src/sessions/session-lifecycle/internal/session-open-execution.ts Outdated
Comment thread src/cli/commands/device.ts Outdated
Comment thread packages/daemon/src/server/__tests__/request-execution-scope.test.ts Outdated
Comment thread src/daemon-entrypoint.test.ts
Comment thread scripts/print-daemon-state-dir.ts Outdated
Comment thread packages/host-kit/src/command.ts Outdated
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 39 files (changes from recent commits).

You’re at about 93% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread src/cli/commands/connection.ts Outdated
Comment thread src/cli/commands/connection.ts Outdated
Comment thread packages/platform-android/src/app-deployment.ts
Comment thread packages/daemon-contracts/src/device-claim-paths.test.ts
…CLI env plumbing

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

The code needs no changes before merge, but the kernel-errors mutation checks are red and must pass first. I reviewed commit 5effcd7.

All five kernel-errors shards fail the Stryker dry run on one test, host-kit/src/code-signature.test.ts:149 ("the daemon source graph stamps the walker that produced it"). That test is unchanged and passes in the unit lanes. The shards passed in run 38965344815. The related-test set for these shards grew from 1372 to 1607 files, which pulled this test in. The new global setup file (https://github.com/callstack/agent-device/blob/5effcd7/src/__tests__/host-environment-setup.ts#L8) imports host-kit/command and so exec.ts and kernel/errors, so I think it caused that growth. I did not reproduce the failure in the sandbox. The walker probably resolves workspace symlinks outside the sandbox root, but I have not proven that. The "Affected decision-kernel mutants" check fails after the dry run ("13 report(s), expected 18"), and Smoke Tests was still running when I looked. No conflicts.

The next step is to remove the global aroundEach setup, or to make that test work in the sandbox. Re-run the kernel-errors shards after that.

Not blocking, and you can take or leave these. The setup file binds every unit test to the live process.env and process.cwd(), so no test can see host_environment_unscoped (exec.ts:140). Please bind createTestHostEnvironment only in suites that enter through the daemon or CLI, and add one test that calls a commandHostEnvironment() consumer outside a scope and expects that reason. Also, createAgentDeviceClient(config, { host }) at https://github.com/callstack/agent-device/blob/5effcd7/src/agent-device-client.ts#L89 uses host only in stateDir(), while the daemon client still reads process.env and process.cwd() (daemon-client-lifecycle.ts:97-140, 818). Either every resolution the client makes should read the same host, or deps.host can wait for #3751. Finally, RequestExecutionScope.host (https://github.com/callstack/agent-device/blob/5effcd7/packages/daemon/src/server/request-execution-scope.ts#L263) has no production reader except its own test, because daemon handlers use the ambient commandHostEnvironment() (19 call sites).

Would one channel per layer be simpler? host-kit would keep the ALS scope only for spawn env and the DEVELOPER_DIR overlay. Daemon handlers would read host from RequestExecutionScope or the runtime deps they already get. Platform code would take host from its existing host or tool-provider object, as AndroidAdbHost.environment does. That would remove the throwing ambient accessor and the test-wide shim, and a missing host would become a type error. #3750, #3751 and #3752 will copy whatever shape this PR sets. Could we settle in #3747 or an ADR whether HostEnvironment travels explicitly or ambiently before they land?

The open inline threads still apply. The two P2 threads on connection.ts (#3763 (comment) on the home path, #3763 (comment) on provider profile) and the P2 thread on bundletool lookup (#3763 (comment)) are still open. One lower-priority thread also applies: #3763 (comment).

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 9 files (changes from recent commits).

You’re at about 94% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread src/__tests__/host-environment-setup.ts Outdated
thymikee and others added 3 commits October 17, 2026 01:05
The global setup made every test related to every mutated source. Suites
that reach host-reading code through neither a daemon request nor the
CLI bind a host with bindTestCommandHost().

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…kages

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… scenarios

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

Pushed d522d257de: the global test setup is gone, so the related-test set no longer grows. Suites that need a host bind one with bindTestCommandHost() (test-kit); a new test pins host_environment_unscoped outside a host. The kernel-errors 3/5 mutation shard passes locally with 1371 related tests (main: 1369). Waiting on CI.

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://callstack.github.io/agent-device/pr-preview/pr-3763/

Built to branch gh-pages at 2026-10-17 16:01 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 97 files (changes from recent commits).

You’re at about 94% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread src/__tests__/cli-capture.ts
@thymikee

Copy link
Copy Markdown
Member Author

Thanks for the PR. The host capture is a good direction, but one defect in the client needs a fix before merge. CI is green at d522d25 (23 checks, none failing), and there are no conflicts.

createAgentDeviceClient now accepts deps.host (or captures one at creation, line 89), and client.stateDir() resolves from host.env.AGENT_DEVICE_STATE_DIR and host.cwd (src/agent-device-client.ts#L215). Every command the same client sends goes through sendToDaemon, which still resolves the state dir from live process.env and process.cwd() (daemon-client-lifecycle.ts#L97, L107) and launches the daemon with process.env (line 818). Two inputs show the gap: createAgentDeviceClient({}, { host: { env: { AGENT_DEVICE_STATE_DIR: '/x' }, cwd } }), and setting process.env.AGENT_DEVICE_STATE_DIR (or calling chdir) after the client exists. Before this PR both reads were live and agreed. Now a public Node API client can report one state dir while it starts and talks to a daemon in another, so tooling that cleans or inspects client.stateDir() acts on the wrong daemon. The rule: everything one client reads must resolve from the same host. Either thread host through the transport factory into resolveClientSettings and startDaemon (explicit state dir, cwd, launch env), or drop deps.host and keep stateDir() on live process reads until #3751 moves the daemon client.

Not blocking, and you can take or leave these: RequestExecutionScope.host is set but nothing in production reads it, because daemon handlers call the ambient commandHostEnvironment() and the test only checks the field is the object passed in (request-execution-scope.ts:275); the MCP route runs inside the bin host scope but createClient calls createAgentDeviceClient(config) without deps.host, so it captures process again (src/mcp/command-tools.ts:251); ensureHarmonyToolchainPathConfigured writes env.PATH on the shared captured host, and captureHostEnvironment does not freeze its copy, so the Readonly type does not stop the write (packages/platform-harmonyos/src/hdc.ts:72); a relative AGENT_DEVICE_CLAIMS_DIR used to resolve against cwd and now throws relative_claims_dir, which the PR body and CHANGELOG do not mention, and the body also names a setup file that does not exist and says 178 files where the diff has 262 (device-claim-paths.ts:35); createTestHostEnvironment repeats captureHostEnvironment by hand (packages/test-kit/src/host-environment.ts:13); and the "request meta cwd or daemon cwd" fallback now reads inline process.cwd() in some daemon sites (screenshot-runtime.ts:326, record-runtime.ts:230/239, the session-script files, session-replay-transaction.ts:65) but commandHostEnvironment().cwd in others (session-perf-runtime.ts:149, session-open-execution.ts:432), so one source or a tracking line in #3751/#3752 would help.

Could the AsyncLocalStorage scope keep only what it already owns, which is spawn env (resolveSpawnEnv, commandDeveloperDir, exec trace), while handlers get the host explicitly? Daemon handlers would read scope.host or a host on their params, CLI ClientCommandHandlers would get ctx.host in the context they already receive, and platform modules would take env from their existing host ports, as Android does with AndroidAdbHost.environment. That would remove the throwing ambient getter and the 89 bindTestCommandHost suites, and make the dead scope.host field the real channel. Before #3750 and #3751 build on this, please decide in #3747 whether "bound HostEnvironment" means an ambient locator or explicit context fields.

I did not trace every public SDK export (artifacts, android-adb, limrun, ai-sdk) to prove none reaches commandHostEnvironment() outside a scope, where it would throw host_environment_unscoped. Unit tests cannot catch that, because 89 suites bind a live host. I also did not run code to check that the scope reaches HTTP connection callbacks and every daemon timer, though the request router rebinds per request. I did not run the tests, so the claim that cli-client-commands.test.ts fails without the harness binding is unverified. No live device run was asked for or checked.

This is reviewed at d522d25. Before merge, the client needs to resolve every state dir read from one host, and the scope question for #3750 and #3751 needs an answer.

createAgentDeviceClient's default transport, the daemon launch and the
CLI run's transport read the host the client was given, not the process.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 11 files (changes from recent commits).

You’re at about 95% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread src/daemon-client/daemon-client-lifecycle.ts Outdated
Comment thread src/__tests__/daemon-registration-owner.test.ts Outdated
Comment thread src/cli.ts Outdated
Comment thread src/daemon-client/daemon-client.ts Outdated
@thymikee

Copy link
Copy Markdown
Member Author

Pushed 756cf4e012 for the client finding: createAgentDeviceClient's default transport is now createDaemonTransport(host), so state-dir resolution, remote and transport settings, the policy check, the daemon launch environment, the provider credential fingerprint and the developer dir all read the client's host. sendToDaemon keeps the released process-level default through a boundary wrapper; the CLI run builds its transport from the run's host. A new test (daemon-client-host.test.ts) gives the client a host that disagrees with process.env and checks that stateDir() and the settings the transport resolves agree.

Also taken from the non-blocking list: createTestHostEnvironment goes through captureHostEnvironment, and the PR body now names the relative_claims_dir refusal and no longer mentions the deleted setup file or the old file count. Not changed: the MCP route's createClient (its host is the same capture as the bin host), the Harmony PATH write (it deliberately changes the environment the daemon spawns with), and the inline process.cwd() fallbacks, which stay in the modules where an import would grow an eager closure; #3751/#3752 own migrating them.

On the ambient versus explicit question: the ambient commandHostEnvironment() exists because platform modules had no host to read and R13 forbids process there. I would like it to stay narrow, covering spawn env and the exec diagnostics, and have #3750 give platform modules their host through the ports they already own (as AndroidAdbHost.environment does), with handlers reading scope.host. That would let #3750/#3751 delete the accessor and the bindTestCommandHost suites as they go. It is your call, and I have asked in #3747 for the decision before they build on this.

thymikee and others added 2 commits October 17, 2026 07:39
…rectory

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

Thanks for the update. Since the review at d522d25 (#3763 (comment)), the client now resolves the policy against host.cwd, but one defect remains at f4616b9.

startDaemon passes env: settings.host.env but no cwd (daemon-client-lifecycle.ts#L832). launchDaemonProcess (daemon-registration-owner.ts#L262) spawns without options.cwd, so the daemon inherits process.cwd(). It then captures that as host.cwd (daemon.ts#L9) and resolves the policy at daemon-runtime.ts#L420, while the client resolves the same relative AGENT_DEVICE_DAEMON_POLICY against its own host.cwd (daemon-client-lifecycle.ts#L327). With createAgentDeviceClient({}, { host: { env: { AGENT_DEVICE_DAEMON_POLICY: 'policy.json' }, cwd: '/client/ws' } }), or a process.chdir() after the client exists, the two sides read different files. A Node SDK client then starts a daemon that exits with "cannot read policy JSON" or loads another file, and the client rejects it with DAEMON_POLICY_MISMATCH. Before this PR both sides read the live process cwd and agreed. The rule is that any relative value in the launch env must resolve the same way in the client and in the daemon. Please add cwd to the launchDaemonProcess input and pass cwd: settings.host.cwd to runCmdDetachedMonitored, which already supports options.cwd. That covers the policy path and any other cwd-relative setting the daemon reads. Please also add a regression where host.cwd differs from process.cwd() and the policy path is relative.

Not blocking, and fine to take or leave: CliDeps now has both sendToDaemon and an optional transportForHost (cli.ts#L70) plus resolveRunTransport. In production transportForHost is always set, so DEFAULT_CLI_DEPS.sendToDaemon and the process-capturing wrapper in daemon-client.ts have no production caller.

Could CliDeps take the transport only as transportForHost(host), defaulting to createDaemonTransport, with sendToDaemon and resolveRunTransport deleted? That would give a run one transport source, and launching with cwd: host.cwd fits in the same DaemonClientSettings.host owner. Nothing has to change first, since sendToDaemon is not a public export and tests can inject transportForHost: () => mock.

Of the open inline threads, the cubic-dev-ai thread on the policy path (#3763 (comment)) still applies until the spawn cwd is fixed. These no longer apply and you can resolve them: the createTestHostEnvironment() use in the registration test (#3763 (comment)), the runCli tests with a non-process host (#3763 (comment)), and the docstring that now names the #3751 upload exception (#3763 (comment)).

I did not run any test or daemon; the daemon cwd finding is traced from the spawn options and the entry capture, and I did not check whether other daemon reads also resolve relative env values against the inherited cwd. Remote artifact upload still reads process env and cwd, which I accepted as deferred to #3751 without tracing it. At f4616b9 Smoke Tests are queued, and Repo Guards & Compatibility and Coverage are still running with no failure yet. This change touches the daemon-client route that smoke runs use, so a failure there would likely be related. There are no conflicts. Before merge, please launch the daemon with cwd: settings.host.cwd, add the regression, and let the checks finish green.

thymikee and others added 4 commits October 17, 2026 09:13
…ment

* origin/main:
  test: move the daemon-composition tests off the daemon's testing exports (#3742)
  fix(android): ignore emulator log lines in -list-avds output (#3743)

# Conflicts:
#	packages/daemon/src/server/__tests__/request-router-fill-text-stdin.test.ts
#	packages/daemon/src/server/__tests__/request-router-macos-app-lease.test.ts
#	packages/daemon/src/server/__tests__/request-router-provider-credentials.test.ts
#	packages/daemon/src/server/__tests__/request-router-record-runtime-lock.test.ts
#	packages/daemon/src/server/__tests__/request-router-recording-health.test.ts
#	packages/daemon/src/server/__tests__/request-router-replay-scope.test.ts
#	packages/daemon/src/server/__tests__/request-router-screenshot.test.ts
#	packages/daemon/src/sessions/request-handler-chain-routing.test.ts
#	src/__tests__/daemon-composition/app-event-delivery.test.ts
#	src/__tests__/daemon-composition/gesture-admission-parity.test.ts
#	src/__tests__/daemon-composition/http-server-limrun-uploaded-apps.test.ts
#	src/__tests__/daemon-composition/request-router-android-perf.test.ts
#	src/__tests__/daemon-composition/request-router-android-snapshot-helper.test.ts
#	src/__tests__/daemon-composition/request-router-open-claim-recovery.test.ts
#	src/__tests__/daemon-composition/request-router-replay-test-ime.test.ts
…g test

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rectory

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… the ambient scope

Handlers, claim writers and lease admission take the host (or its env) from the request
execution scope, runtime-owned timers take it at construction. commandHostEnvironment() stays
for command execution until the platform and root packages convert.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 101 files (changes from recent commits).

You’re at about 96% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread packages/daemon/src/sessions/handlers/session-app-source-deployment.ts Outdated
Comment thread packages/daemon/src/sessions/request-handler-chain.ts Outdated
Comment thread packages/daemon/src/server/http/daemon-session-idle-expiry.test.ts Outdated
…n test envs

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

The code is ready at 225e0bd. The merge conflict from the earlier review is gone, and the three inline fixes from the other reviewer are in at this commit.

Not blocking: resolveRunTransport runs twice per run (cli.ts:223 and cli.ts:438), so a run with a remote config and a local daemon builds two ensure-once maps, where main ensured once. Resolving the transport once in runCliOnHost and passing it to both clients would fix it. In MCP, createClient (src/mcp/command-tools.ts:253) calls createEnsuredDaemonTransport() without a host, so each tool call captures the process host three times, which differs from the PR body's claim that a client reads one captured host. Capturing once per tool call and passing that host to the transport and the client would match the claim. Take or leave both.

On the inline threads: all three from the other reviewer are fixed at 225e0bd, so please resolve them: #3763 (comment) (unused host removed from the install-from-source route), #3763 (comment) (doc comment now names only the routes that read the host), and #3763 (comment) (idle-expiry tests now use the test host environment).

Smoke Tests is still running on 225e0bd with no failure. The diff touches the route it exercises (CLI to daemon launch, and daemon request handling), so a failure there would need a look against these changes. I did not run any tests locally, so your gate claim for this commit is unverified by me. I also did not re-check the inline process.cwd() and process.env reads left in packages/daemon, which the PR body assigns to #3750 and #3751. Before merge, Smoke Tests must finish green on 225e0bd.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 17, 2026
thymikee and others added 2 commits October 17, 2026 12:42
…l call; default the lease provider cwd to the bound host

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ment

* origin/main:
  refactor(daemon): publish a single package entrypoint (#3781)
  fix(daemon-client): wait for an acknowledged quiesce instead of killing the daemon (#3780)
  fix(daemon): refuse leases for providers without a registered runtime (#3773)

# Conflicts:
#	packages/daemon/src/sessions/handlers/__tests__/lease.test.ts
#	src/daemon-client/daemon-client-ensure.ts
@thymikee

Copy link
Copy Markdown
Member Author

Pushed 916db0b9a4 (check:affected passed on it): current main merged (#3773 included), plus the reviewer's recommendations.

  • One transport per CLI run. runCliOnHost resolves the transport once and hands it to the remote-materialization client and the command client, so a run has a single ensure-once map.
  • One host per MCP tool call. createClient captures the host once and passes it to both createEnsuredDaemonTransport({ host }) and createAgentDeviceClient(config, { host }).
  • Lease provider cwd. lease_allocate, lease_heartbeat and lease_release fill the provider context's cwd from the bound host when the client sent none (test: lease.test.ts).
  • Threads. The three cubic threads are fixed and resolved.
  • Not done here: narrowing commandHostEnvironment() to command execution. Every non-test call site in packages/daemon, host-kit, kernel and daemon-contracts now reads an explicit host. The remaining sites are in the root src/ readers (refactor(host): read host settings from HostEnvironment in root src and remaining packages #3775) and the platform packages (refactor(platforms): read host settings from HostEnvironment in platform and capture packages #3777). Removing the export now would break those branches, so I will narrow it once both have converted.

@thymikee

Copy link
Copy Markdown
Member Author

The PR is ready at 916db0b. Both earlier cubic threads that the delta touched are fixed, and no blocking issues remain.

Not blocking, and fine to take or leave: the field doc at https://github.com/callstack/agent-device/blob/916db0b/packages/daemon/src/sessions/request-handler-chain.ts#L34 still lists only the session and record-trace routes, but the lease route now reads host too (line 157), so the doc could name it or just say host is the request's bound host. Also, no test pins the one-transport-per-run hoist or the single MCP host capture, so a runCli test with remoteConfig could assert transportForHost is called exactly once (today https://github.com/callstack/agent-device/blob/916db0b/src/daemon-client/daemon-client-host.test.ts#L62 only checks it gets the host).

Of the open inline threads, none are P1 or P2 and still apply, and no lower-priority ones apply. These do not apply and can be resolved: the unused host parameter in session-app-source-deployment.ts is gone since 225e0bd (#3763 (comment)); the idle-expiry test fixtures now use createTestHostEnvironment().env (#3763 (comment)); and the request-handler-chain.ts:34 rewording is superseded by the note above (#3763 (comment)).

CI is green at 916db0b: 23 checks, none failing. I did not run the tests myself, and I judged the lease.test.ts regression coverage from a read of the code before the change. I did not re-review the content of #3773, #3780 and #3781, only how their merge interacts with the host plumbing. Nothing else stands between this PR and a human merge.

…he bound host

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

Pushed 9a4e0812f4 (check:affected passed on it) for the 11:38Z recommendations.

  • Field doc. RequestHandlerChainParams.host now says it is the request's bound host, which the lease route reads too.
  • One transport per CLI run, pinned. cli-run-transport.test.ts runs a remote-materialization open through runCli and asserts transportForHost is called exactly once while the materialization client's lease_allocate and the command client's requests both go through it. Undoing the hoist makes it fail.
  • One host per MCP tool call, pinned. command-tools-daemon-lifecycle.test.ts asserts one captureHostEnvironment per tool call and that the host ensureDaemon receives is the captured object.
  • Threads. No inline thread is open on this PR.
  • Still pending: narrowing commandHostEnvironment(). Its remaining non-test call sites are the root src/ readers (refactor(host): read host settings from HostEnvironment in root src and remaining packages #3775) and the platform packages (refactor(platforms): read host settings from HostEnvironment in platform and capture packages #3777); I narrow the export once both have converted.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files (changes from recent commits).

You’re at about 97% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread src/__tests__/cli-run-transport.test.ts Outdated
thymikee and others added 4 commits October 17, 2026 14:10
…transport test

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ment

* origin/main:
  feat(provider-testmu): publish @agent-device/testmu on plugin contract v2 (#3770)
  docs(plugins): document plugin contract v2, the inventory and ADR 0034 (#3769)
  feat(daemon): daemon status lists the registered provider plugins (#3768)
  feat(plugins): plugin contract v2 and one provider registry (#3767)

# Conflicts:
#	packages/daemon/src/sessions/handlers/lease.ts
#	src/__tests__/cloud-connect-testmu.test.ts
#	src/cli/commands/connection.ts
#	src/cli/commands/daemon.ts
#	src/daemon-client/daemon-client-metadata.ts
#	src/daemon-client/daemon-client.ts
…ment

* origin/main:
  fix(ios-runner): stop wide sliders from wedging tree snapshots (#3776)
… leaving their shared prologue untouched

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

This PR is ready at c9aa2f2. The test changes since the earlier review (916db0b) look good, and the doc comment now says the request's bound host, which every route reads.

Not blocking: in src/tests/cli-run-transport.test.ts (https://github.com/callstack/agent-device/blob/c9aa2f2/src/__tests__/cli-run-transport.test.ts#L65) a failure other than 'exit' after lease_allocate still shows up only in an assertion message that already passes, the test never asserts the expected exit code, and fs.rmSync sits outside a finally block so a failing run leaks the temp workspace. You can take or leave these.

Both open inline threads from the earlier round are fixed at this commit and can be resolved: the doc comment on the request handler chain (#3763 (comment)) and the runCli error in the lease_allocate assertion (#3763 (comment)). The exit code is still not asserted, as noted above.

The Smoke Tests and Coverage checks are still queued or in progress, and none has failed. The new changes touch only tests and one doc comment, so they do not overlap with the smoke route. The full PR does change the CLI to daemon host plumbing that the smoke tests exercise, so a smoke failure would need a closer look. No conflicts are known. The PR needs those two checks to pass at c9aa2f2 before merge.

I read the code and did not run the new tests. That the vi.spyOn on the @agent-device/host-kit/process namespace takes effect for the dynamic import in command-tools.ts rests on the author's check:affected report and on the same spy pattern working in daemon-registration-owner.test.ts. Code merged from main still reads process.env by default, for example connectCommand's primeProviderConnection and listPlugins(process.env) in plugins.ts. That predates this change and is tracked in #3750 and #3751, and under the CLI it has the same values as the captured host. The request-router redaction test I asked for earlier is not in these changes; the PR only adds a createTestHostEnvironment() host fixture there.

thymikee and others added 2 commits October 17, 2026 15:47
…t cache its process already had

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ys remove its workspace

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files (changes from recent commits).

You’re at about 97% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread test/integration/provider-scenarios/harness.ts
thymikee and others added 3 commits October 17, 2026 15:57
…h of its env override

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* origin/main:
  fix(daemon): scope a transient device claim to the session that holds it (#3714)

# Conflicts:
#	packages/daemon/src/resources/device/__tests__/device-claim-admission.test.ts
#	packages/daemon/src/server/request-execution-scope.ts
… cleared it

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

Pushed d8a9bd40c0 (check:affected passed on it) for the 13:35Z recommendations, with current main (#3714 included).

thymikee and others added 2 commits October 17, 2026 16:51
…nd remaining packages (#3775)

* refactor(host): thread the client host through remote artifact preparation and materialization

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(host): pass the composition's env to the platform runtime, diagnostics, toolchain and deployment hosts

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(host): refuse a relative provider app path that names no working directory

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(host): require the host's env and cwd where the CLI resolves options, binding and auth

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(host): keep the upload client suite under its size ratchet

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(host): pass the run's host to CLI command handlers

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(host): bind host-taking commands once in a shared helper

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(changelog): resolveHubAppReference needs cwd for a relative app path

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(host): give the new handler suites a host

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ment

* origin/main:
  fix: align replay, SDK, and session boundaries with ADRs (#3762)
  fix(daemon-client): document the drain timeout and tighten ensureDaemon follow-ups (#3783)

# Conflicts:
#	CHANGELOG.md
@thymikee

Copy link
Copy Markdown
Member Author

The code is ready for review at 870cf66. The earlier review (#3763 (comment)) found no problems, and the new #3775 changes in this round do not change that. The harness env ordering is now fixed, so the cubic P2 thread on harness.ts:119 no longer applies, so you can resolve it.

Not blocking, and you can take or leave these: the harness builds its gateway with env: process.env at https://github.com/callstack/agent-device/blob/870cf66/test/integration/provider-scenarios/harness.ts#L151 while its daemon host uses the scenario env, so passing input.host.env would match production (src/daemon-composition.ts:95) and let you drop the process-level PATH stub; the abandon call at https://github.com/callstack/agent-device/blob/870cf66/packages/daemon/src/resources/device/device-claim-admission.ts#L131 has no test that fails if it reverts to process.env, so the failed-release test could use a claims dir that differs from process.env and assert the record lands there; and the PR body still says non-owned root src keeps inline reads for #3751 and names validation commit 225e0bd, so it could mention #3775 and the current head.

The only open item is CI. Smoke Tests are queued or running with no failure yet. #3775 changes the root composition env and the CLI handler host, which the smoke route uses, so a smoke failure here would need a look. Smoke Tests must pass at 870cf66 before merge.

I did not run tests. I reviewed #3775's 98 files for new process.env or cwd reads, layer crossings and the resolveHubAppReference contract, but not its test call-site churn line by line. The changes merged from main (#3776, #3714, #3762) are upstream, and I checked them only where they meet host plumbing.

… host claims directory on a failed release

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files (changes from recent commits).

You’re at about 97% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="test/integration/provider-scenarios/ios-record-trace.test.ts">

<violation number="1" location="test/integration/provider-scenarios/ios-record-trace.test.ts:116">
P3: This comment attributes the remaining process reads to 'the root helpers' moving in #3777, but root `src/` was already migrated in #3775, which is merged into this branch. Only the capture-kit reads (e.g. `swift-cache.ts` reading `AGENT_DEVICE_SWIFT_CACHE_DIR`) still depend on #3777, so drop 'and the root helpers' from the comment.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

appleRunnerProvider: () => appleRunnerProvider,
appleToolProvider: () => appleTool.provider,
appleSimulatorScreenRecordingTransport: () => recordingTransport,
deviceInventoryProvider: async () => [PROVIDER_SCENARIO_IOS_SIMULATOR],
});
// The recording finalizer and Swift helper still read PATH and the cache from the process
// (capture-kit and the root helpers move to the host in #3777), so the scenario sets both.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This comment attributes the remaining process reads to 'the root helpers' moving in #3777, but root src/ was already migrated in #3775, which is merged into this branch. Only the capture-kit reads (e.g. swift-cache.ts reading AGENT_DEVICE_SWIFT_CACHE_DIR) still depend on #3777, so drop 'and the root helpers' from the comment.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At test/integration/provider-scenarios/ios-record-trace.test.ts, line 116:

<comment>This comment attributes the remaining process reads to 'the root helpers' moving in #3777, but root `src/` was already migrated in #3775, which is merged into this branch. Only the capture-kit reads (e.g. `swift-cache.ts` reading `AGENT_DEVICE_SWIFT_CACHE_DIR`) still depend on #3777, so drop 'and the root helpers' from the comment.</comment>

<file context>
@@ -112,6 +112,8 @@ test('iOS simulator recording reports host contention and recovers through the f
         deviceInventoryProvider: async () => [PROVIDER_SCENARIO_IOS_SIMULATOR],
       });
+      // The recording finalizer and Swift helper still read PATH and the cache from the process
+      // (capture-kit and the root helpers move to the host in #3777), so the scenario sets both.
       const previousPath = process.env.PATH;
       const previousSwiftCacheDir = process.env.AGENT_DEVICE_SWIFT_CACHE_DIR;
</file context>
Suggested change
// (capture-kit and the root helpers move to the host in #3777), so the scenario sets both.
// (capture-kit moves to the host in #3777), so the scenario sets both.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(host-kit): capture HostEnvironment once at entry points

1 participant