Skip to content

feat(cli): release the proxy lease after close - #3477

Draft
V3RON wants to merge 1 commit into
mainfrom
feat/cli-release-lease-after-close
Draft

V3RON wants to merge 1 commit into
mainfrom
feat/cli-release-lease-after-close

Conversation

@V3RON

@V3RON V3RON commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

In proxy mode the daemon ends a session's lease on close, but no release goes over the wire, so a gateway in front of the daemon never learns the lease ended. The CLI also kept the old lease id, so the next open heartbeated it, got LEASE_NOT_FOUND, and allocated a new lease.

Now, after a successful proxy close, the CLI sends lease_release for its stored lease and removes the lease from its local connection state. A failed or "not found" release does not fail close. Nothing changes for leases the daemon keeps on close (retainOnClose, now recorded from allocate/heartbeat), host-allocated macos-app leases, non-proxy connections, or local mode. 6 files; the remote-proxy docs gain one sentence.

Validation

Tested commit 6e8d5d128: pnpm check:affected --run passed (format, lint, typecheck, layering, fallow, build, docs; 44 related test files, 441 tests).

New tests in cli-close.test.ts. The two release tests fail without the fix. The retain, macos-app, and non-proxy tests each fail when their guard is removed.

Live run: built CLI → logging relay → agent-device proxy → local daemon, using a private iOS simulator:

open   lease.allocate → e45eefd6…
close  command=close; lease.release e45eefd6… → {"released":false}; local lease: none
open   lease.allocate → fe83d4d1… (no heartbeat)

On origin/main the same flow sends no release after close, and the next open gets lease.heartbeat → 401 LEASE_NOT_FOUND and then allocates again.

With the change, disconnect on an open session still sends close then lease.release. Local-mode open/close works as before.

View guided diff Turn on auto-fix

In proxy mode the daemon ends a closed session's lease itself, so a host
fronting the daemon never saw a release, and the CLI kept the stale lease
id: the next open heartbeated it, got LEASE_NOT_FOUND and reallocated.

After a successful close the CLI now sends lease_release for its stored
proxy lease (any failure is ignored) and drops the lease from its local
connection state. Leases the daemon retains on close, host-allocated
macos-app leases and non-proxy connections are left alone.
@github-actions

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.17 MB 5.17 MB -1.8 kB
Package (unpacked) 5.17 MB 5.17 MB -1.8 kB
Package (download) 1.55 MB 1.55 MB -811 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.3 ms 27.5 ms +0.1 ms
CLI --help 78.3 ms 77.9 ms -0.4 ms

@github-actions

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-3477/

Built to branch gh-pages at 2026-10-12 09:52 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@thymikee

Copy link
Copy Markdown
Member

The code needs a design decision and live evidence before it is ready to merge. At 6e8d5d1 I found no blocking defect, but the daemon should own the lease outcome instead of a cached CLI flag. I did not re-run the live relay run or the tests, and the live claim comes from the PR body. Smoke Tests is still queued, not failing, and there are no conflicts that I know of. The diff touches the top-level CLI close route that smoke flows run, but for local close releaseProxyLeaseAfterClose returns at once because flags.remoteConfig is unset.

Could the daemon's close success response (buildCloseSuccessResponse in src/daemon/session-lifecycle/internal/session-close.ts) report the lease outcome (released, retained, or none, plus the leaseId)? The CLI would then clear its stored leaseId when the response says released, on every route that runs close, batch included. A gateway that must see lease ends would read that outcome from the close reply it already relays, or the daemon would emit the release itself. Either way, leaseRetainOnClose, its validator, the leaseStateMatches comparison and the command-name hook in src/cli.ts would go. This also covers the cases the CLI-side check misses today. Examples are a batch with a close step, a close from MCP, Node or the AI SDK, a same-client allocate with retainOnClose after the last heartbeat, and a close whose platform teardown fails after the lease is released. What would have to change first is a decision on whether the gateway's lease view is a daemon or proxy contract. An ADR 0007 note would define the signal once on the daemon response.

Not blocking, and you can take or leave these: the skip decision at https://github.com/callstack/agent-device/blob/6e8d5d1/src/cli/commands/connection.ts#L356 reads a CLI-side copy of retain state from the last heartbeat, while the daemon decides at close time, so a stale copy can end a lease the daemon just retained. The hook at https://github.com/callstack/agent-device/blob/6e8d5d1/src/cli.ts#L188 keys on the top-level command name, so batch and failed-close paths skip the release and the local clear. The bare catch at https://github.com/callstack/agent-device/blob/6e8d5d1/src/cli/commands/connection.ts#L360 swallows auth and network errors with no diagnostic, so a diagnostic with the normalized error code would help.

#3479 edits connection-runtime.ts and the same remote-proxy.md paragraph, so expect a conflict there. I read that from its description and file list, not from merged code. #3480 makes lease end tear down the session, so the two can land in either order, but it does not remove the need for a daemon-owned outcome. Whether any real host upgrades a CLI-allocated lease to retainOnClose mid-session depends on the gateway outside this repo, which I cannot see.

Before merge, we need a maintainer decision on whether the close response should own the lease outcome, and Smoke Tests needs to finish.

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.

2 participants