Skip to content

feat(client): daemonLifecycle attach mode and ensureDaemon() - #3765

Open
thymikee wants to merge 1 commit into
feat/ensure-daemonfrom
feat/ensure-daemon-client
Open

thymikee wants to merge 1 commit into
feat/ensure-daemonfrom
feat/ensure-daemon-client

Conversation

@thymikee

@thymikee thymikee commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Summary

Layer 2 of 3 for #3754 (umbrella #3747); based on #3764. The client half: who owns the daemon is now an explicit choice.

  • createAgentDeviceClient({ daemonLifecycle: 'attach' | 'managed' }); managed is the default and behaves as before (additive in 0.21.x).
  • attach connects to the registered daemon and never starts, replaces or kills one, including after a timeout. A missing daemon fails with details.reason: 'daemon-not-running', an incompatible one with 'daemon-incompatible' (both identities, hint names agent-device daemon stop).
  • ensureDaemon({ stateDir, onStart }) starts a missing daemon and replaces an incompatible one only through the quiesce request from feat(daemon): quiesce request that retires an idle daemon atomically #3764. A busy daemon fails daemon-busy, a daemon without the capability is never replaced, and a live daemon on another transport is quiesced, not killed. A daemon that times out on quiesce is force-retired only after repeated failed liveness probes.
  • A remote daemonBaseUrl connection never starts or retires a local daemon in either mode; ensureDaemon reads no remote env.
await ensureDaemon({ stateDir });
const client = createAgentDeviceClient({ stateDir, daemonLifecycle: 'attach' });

26 files incl. docs and changelog. The repo has no changeset tooling, so the CHANGELOG.md Unreleased entry is the changeset. Layer 3 adopts this in the CLI and MCP.

Validation

pnpm check:affected --run --head 0c26950c21: passed. New tests cover attach (missing, incompatible, unreachable, timeout), ensureDaemon (start, idle replace, busy, old daemon, transport, contention, unconfirmed retirement) and remote.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.32 MB 5.34 MB +10.9 kB
Package (unpacked) 5.32 MB 5.33 MB +10.9 kB
Package (download) 1.60 MB 1.61 MB +3.4 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 25.6 ms 26.2 ms +0.6 ms
CLI --help 73.0 ms 71.8 ms -1.2 ms

@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-3765/

Built to branch gh-pages at 2026-10-17 01:47 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 26 files

You’re at about 92% 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-ensure.ts Outdated
Comment thread src/daemon-client/daemon-client-quiesce.ts Outdated
Comment thread CHANGELOG.md Outdated
Comment thread website/docs/docs/client-api.md Outdated
Comment thread src/agent-device-client.ts
Comment thread src/cli/commands/proxy.ts Outdated
Comment thread packages/daemon-contracts/src/daemon-resolution.ts Outdated
Comment thread src/daemon-client/__tests__/daemon-client-explicit-lifecycle.test.ts Outdated
Comment thread src/daemon-client/daemon-client-ensure.ts
Comment thread website/docs/docs/client-api.md

@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 2 files (changes from recent commits).

You’re at about 92% 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 website/docs/docs/client-api.md Outdated
@thymikee
thymikee force-pushed the feat/ensure-daemon-client branch from 9f6aff4 to 4488553 Compare October 16, 2026 21:35
@thymikee
thymikee force-pushed the feat/ensure-daemon-client branch 2 times, most recently from 685c01a to c95c9c8 Compare October 16, 2026 22:28
@thymikee
thymikee force-pushed the feat/ensure-daemon-client branch from c95c9c8 to 44eefce Compare October 16, 2026 23:05

@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 14 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/daemon-client/__tests__/daemon-client-explicit-lifecycle.test.ts Outdated
Comment thread CHANGELOG.md Outdated
Comment thread src/daemon-client/__tests__/daemon-client-explicit-lifecycle.test.ts Outdated
Comment thread packages/contracts/src/client-connection.ts Outdated
Comment thread src/daemon-client/daemon-client-quiesce.ts Outdated
@thymikee
thymikee force-pushed the feat/ensure-daemon-client branch from 44eefce to 5035bed Compare October 16, 2026 23:21
@thymikee
thymikee force-pushed the feat/ensure-daemon-client branch 2 times, most recently from 769eda3 to ddb6a5e Compare October 17, 2026 00:14
@thymikee
thymikee force-pushed the feat/ensure-daemon-client branch from ddb6a5e to e9819db Compare October 17, 2026 00:38
@thymikee
thymikee force-pushed the feat/ensure-daemon-client branch from e9819db to ad96e33 Compare October 17, 2026 01:33
An attach client connects to the daemon registered in its state directory and never starts,
replaces or kills one, including after a request times out; it fails with daemon-not-running or
daemon-incompatible. ensureDaemon() starts a missing daemon and replaces an incompatible one only
through the daemon's own quiesce answer, refusing with daemon-busy while it holds work and never
replacing a daemon that predates the capability. managed stays the default and unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@thymikee
thymikee force-pushed the feat/ensure-daemon-client branch from ad96e33 to 0c26950 Compare October 17, 2026 01:44
@thymikee

Copy link
Copy Markdown
Member Author

The reviewed commit 0c26950 has one problem that should change before merge. agent-device proxy now fails where it used to replace the daemon.

src/cli/commands/proxy.ts:32 used to call the managed ensureDaemon, which replaced an incompatible or socket-only daemon. It now calls ensureDaemonInfo. Three cases now fail: a daemon from the previous release with no quiesce capability (daemon-incompatible), an incompatible daemon that holds a session or lease (daemon-busy), and a live socket-only daemon that cannot be quiesced. The first case is the normal state right after an upgrade. A CLI user who runs agent-device proxy with the old daemon still running gets a refusal and must run daemon stop by hand. The PR body, the CHANGELOG line "managed stays the default and behaves as before", and remote-proxy.md do not mention this. Issue #3754 puts the CLI's move to idle-only replacement in layer 3, with its own changelog entry. The rule: no CLI surface changes ownership semantics in this additive layer. Please keep proxy.ts on ensureManagedDaemon and move the proxy switch into layer 3 with the CLI and MCP adoption. If it stays here, add a CHANGELOG entry and a remote-proxy.md note for the refusal.

Not blocking, and fine to take or leave: the rule "may this caller start or retire the daemon" is now encoded four times (daemonLifecycle, retireIncompatibleContender, the sendRequest timeoutRecovery, and handleRequestTimeout's retireUnresponsive); daemonStopCommand in daemon-client-timeout.ts duplicates stopHint and the inline strings in daemon-client-lifecycle.ts; the attach test in daemon-client-explicit-lifecycle.test.ts only checks the timeoutRecovery value on a spy, and every quiesce outcome comes from a scripted /rpc stub rather than the #3764 handler, so one sendToDaemon attach test with a wedged daemon and one ensureDaemon test that replaces a real spawned daemon would close that gap; the quiesce timeout in daemon-client-ensure.ts can rethrow the attach-worded hint "start one with ensureDaemon()" and needs its own hint; and the PR body says 26 files while the diff has 29.

Simplicity question: could daemonLifecycle, retireIncompatibleContender and timeoutRecovery/retireUnresponsive become one DaemonClientSettings field, authority: 'managed' | 'attach' | 'ensure', passed to sendRequest, with the contender rule, the timeout retire and the timeout hint all derived from it in one place? attachLocalDaemon and ensureOnce would then share one switch over observeLocalDaemon's decision. That drops about 30 lines and makes the invalid combinations impossible to write. It stays inside src/daemon-client, so no ADR is needed, and it is best done before layer 3 adds CLI and MCP callers that would otherwise set all four markers.

CI has no result on this head. All 9 checks that did not pass were cancelled as superseded runs on the stacked branch, and the diff touches each of those lanes, so please rerun them. I read the code and did not run the tests or check:affected, so the "passed" in the PR body is unverified by me. No test drives ensureDaemon against the real #3764 daemon_quiesce handler, so the client and daemon pairing rests on contract constants only. There are no conflicts. Before merge, keep proxy.ts on ensureManagedDaemon (or document its new idle-only replacement in the CHANGELOG and remote-proxy.md) and rerun the cancelled CI lanes on this head.

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant