Skip to content

agent-device front ignores endedLeases, so close no longer frees the device #607

Closed

Description

@V3RON

Found by the slow lane on PR #606 (stack #600). The stack does not change the code involved, so main has this bug too since #603.

What happens

e2e/slow-agent-device.test.ts, the iOS test that ends with close, then waits for the device to be free. close returns 200 and the agent-device session ends. But the simlock lease stays leased, and no release reaches simlock. The device is freed only when the 300 s lease TTL runs out, too late for the test's 330 s wait.

Why

agent-device 0.21.28 (callstack/agent-device#3477) no longer sends lease_release after close. Instead, the daemon's command reply carries endedLeases: [{ leaseId, outcome }]: in data on success, in error.details on failure, and in data next to results for a batch. The gateway in front of the daemon is expected to end its own lease from it. simlock's agent-device front never reads endedLeases (grep -rn endedLeases src is empty). It releases only on an explicit lease.release RPC (src/agent-device/front/release.ts).

Likely fix

In the front's command relay, read endedLeases from all three places. For each lease this token holds, release it in simlock and mark it ended by the agent. The fake in src/agent-device/testing.ts needs to return endedLeases on close.

Written by an agent.

Activity

  1. added
    bug:newReported. Nobody has looked yet.
    bug:triageAn agent may reproduce it and write the triage report.
    and removed
    bug:newReported. Nobody has looked yet.
    on Oct 15, 2026
  2. self-assigned this
    on Oct 15, 2026
  3. V3RON commented on Oct 15, 2026

    @V3RON
    ContributorAuthor

    Reproduction

    Defect, confirmed as reported. On bug/607-repro, src/agent-device/edge.test.ts, a table of three cases: "%s: a command that ends the agent-device lease and names it in endedLeases releases the simlock lease and frees the device though the CLI sends no release, and a later heartbeat answers LEASE_NOT_FOUND". The cases are close (in data), a failed close (in error.details), and a batch with a close step (beside results). FakeAgentDevice now does what 0.21.28 does: it ends the lease on close and reports it.

    pnpm exec vitest run --project unit src/agent-device/edge.test.ts -t endedLeases
    
    × all 3 cases
    AssertionError: expected [ '00000000000000000000000000000001' ] to deeply equal []
     ❯ src/agent-device/edge.test.ts:1256   await expect.poll(async () => listed(rig)).toEqual([]);
    

    The other 209 tests in that file and the gateway, lease-end and main tests still pass with the changed fake.

    Root cause

    src/agent-device/front/front.ts:150 sends every command through runCommand, which hands back agent-device's reply unread (:169). The only code that frees the simlock lease when the agent ends a lease is release.ts:24, and it runs only on an explicit lease.release RPC. agent-device 0.21.28 no longer sends that RPC after close; it reports the end in endedLeases instead (its client-types.d.ts, EndedLease). So the lease stays until its TTL runs out. docs/HTTP-API.md:1580 promises that close frees it.

    Simplest fix

    Move release.ts:17-28 (map entry check, releaseSimlock, mark ended by agent, cancel renewals) into one function. In runCommand, read the reply and collect endedLeases from result.data and from error.data.details. Call that function for each id the map holds for this token. Gateway and worker share this front, so both paths are covered.

    Alternatives rejected

    Risk

    • A streamed (ndjson) reply has its result in the last frame. Reading it must not hold back the stream (untilBodyEnds).
    • endedLeases is wire input. Act only on leases the map holds for this token.
    • The release scope stays recorded, so lease-end sends agent-device a second release. Check that it answers released: false and does not delay the reclaim, or drop the scope.

    Written by an agent.

  4. removed their assignment
    on Oct 15, 2026
  5. added
    bug:readyAn agent may fix it.
    and removed
    bug:triageAn agent may reproduce it and write the triage report.
    on Oct 15, 2026
  6. self-assigned this
    on Oct 15, 2026
  7. removed their assignment
    on Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug:readyAn agent may fix it.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions