Repository navigation
feat(cli): release the proxy lease after close - #3477
Conversation
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.
Size Report
Startup median (7 runs, lower is better):
|
|
|
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 Could the daemon's close success 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 Before merge, we need a maintainer decision on whether the close response should own the lease outcome, and Smoke Tests needs to finish. |
* origin/main: (83 commits) feat(ios): interpret React Native accessibility value semantics (#3589) fix(android): reinstall and reselect the test IME helper after it disappears (#3606) feat(snapshot): carry provider keyboard observations through acquisition (#3593) chore(web): bump managed agent-browser backend to 0.39.0 (#3591) fix(fold): report a dispatched pose as unverified when devicectl has no hinge-angle stream (#3587) feat(android): expose system-bar membership on snapshot nodes (#3585) fix(android): align ADB and Appium field values (#3584) test(closure): read committed JSON modules on the merge-base side (#3602) fix(ci): preserve manual Android suite timing (#3554) refactor(daemon): make the layer folders the declaration (PR 10) (#3520) refactor(move): extract the daemon server into @agent-device/daemon (11b) (#3546) refactor(daemon): package-local runtime fakes for the remaining daemon ports [11a-4] (#3544) refactor(daemon): cut daemon test edges into root data fixtures and fakes [11a-3] (#3517) refactor(11a-2): package-local fakes at the daemon ports; root-own command-surface composition tests (#3519) test(replay): verify Settings queries and bound scrolling (#3560) feat(snapshot): measure Android Appium viewport (#3586) fix(ios): verify a fill whose unidentified field moved while it kept focus (#3596) refactor(coverage): one fallow config with per-function CRAP ceilings (#3588) test: bound CLI promotion cases and align provider budget (#3590) fix(metro): report what the dev-server stop proved instead of assuming success (#3575) ...
Replace the bare catch with a warn diagnostic that carries the normalized error code and reason, so auth and network failures are visible.
Session close now says what it did with the session's daemon lease:
`lease: { outcome: 'released' | 'retained', leaseId }` or
`{ outcome: 'none' }`. A close that fails after the release or retention
carries the same field in its error details. The shape is declared once
as SessionCloseLeaseOutcome in contracts, and the Node client returns it
from sessions.close() and apps.close(). ADR 0007 records that the close
reply owns the lease outcome.
The CLI no longer caches retainOnClose or sends lease_release after close. The daemon already ends the lease on close and reports it, so the CLI transport reads every reply of a remote connection and forgets the stored proxy lease when a close reply, a failed close, or a batch close step reports it released. This covers batch and failed-close routes that the top-level close hook missed.
|
New head is a7dc1d6. Main was merged in at 74921c8 with no conflicts.
Checks run: |
|
The close reply now carries the lease outcome on the direct route, and the lease_release round trip from 6e8d5d1 is gone. One gap remains on replay and test routes, and the evidence is still missing on a7dc1d6. closeReplayTestSession runs the close with the caller's lease, but it returns void and drops the reply's lease field. A replay script's close step goes through the replay invoke, so its reply data never reaches the outer response. For proxy The PR body's live run is from 6e8d5d1, which had the old lease_release design. This head changes the daemon close reply and the CLI reply path in closed-lease.ts, so please run the built CLI through a logging relay into I reviewed the 18-file logical diff from cfa9653 to a7dc1d6 and did not run tests, check:daemon-wire-compat or a live proxy. The test results I know of come from your comment. I traced closeReplayTestSession passing req.meta, but I did not confirm how replay builds the wire request for each step. Whether a real gateway reads CI is still pending: two Smoke Tests jobs are queued and Compatibility & Provenance is running, and none has failed. The diff touches what both check, since local close now gets lease:{outcome:'none'} in every reply, and the close reply gains a field. The cli.ts wrapper returns early when remoteConfig is unset, so the local CLI path is unchanged. The PR also has merge conflicts with main. Before merge it needs the live proxy run on this head and a fix or documented exclusion for the replay and test close routes. |
…e-after-close Keep both ADR 0007 rules: #3480's lease end ends the session, and the close reply owns the lease outcome. A close that finds its lease already inactive reports released.
Every daemon reply to a request that ended a session lease now lists it in endedLeases at response level (data on success, error.details on failure). The outermost request owns the ledger; batch steps, replay and test script closes, and test session cleanup record on it, so proxy replay/test of a script that closes tells the CLI and a relaying gateway the lease ended. The per-close lease field and SessionCloseLeaseOutcome are gone, and the CLI reads only the response-level field.
|
New head is e38482f. It fixes the replay and test gap. Each daemon reply to a request that ended a session lease now lists that lease once at response level, as Live run on e38482f. The setup was: built CLI → a small logging relay → The I did not do a live run for Checks on e38482f: |
|
This is ready for review at e38482f. Every reply to a request that ended a session lease now lists it once as Not blocking: a There are no conflicts. CI is still queued on e38482f. Because the change wraps the outermost request handler, check any Smoke, Integration or Compatibility failure against |
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 nextopenheartbeated it, gotLEASE_NOT_FOUND, and allocated a new lease.Now, after a successful proxy
close, the CLI sendslease_releasefor its stored lease and removes the lease from its local connection state. A failed or "not found" release does not failclose. Nothing changes for leases the daemon keeps on close (retainOnClose, now recorded from allocate/heartbeat), host-allocatedmacos-appleases, non-proxy connections, or local mode. 6 files; the remote-proxy docs gain one sentence.Validation
Tested commit
6e8d5d128:pnpm check:affected --runpassed (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:On
origin/mainthe same flow sends no release afterclose, and the nextopengetslease.heartbeat → 401 LEASE_NOT_FOUNDand then allocates again.With the change,
disconnecton an open session still sendsclosethenlease.release. Local-modeopen/closeworks as before.