Skip to content

feat: remote session close via HTTP with running guard and self-healing - #61

Merged
william0wang merged 2 commits into
mainfrom
feat/remote-session-close
Aug 20, 2026
Merged

feat: remote session close via HTTP with running guard and self-healing#61
william0wang merged 2 commits into
mainfrom
feat/remote-session-close

Conversation

@william0wang

Copy link
Copy Markdown
Owner

Background

Remote lists accumulated conversations the editor side had long retired: ACP has no editor→agent "tab closed" notification, so the bridge's sessionSummaries (the membership source for both the heartbeat and /status) never loses an entry once it gains activity — the only cleanup was a bridge restart.

Requested semantics: sessions the editor still has open must NOT be closable; closing means retiring from the remote list, not deleting anything.

Design (ADR-0006)

"Editor side still open" is unobservable from the bridge (Zed sends nothing on tab close), so the guard is two mechanisms instead of a precondition:

  • Running guard (409) — a pending turn is the one reliable in-use signal; refuse and tell the client to cancel first.
  • Self-healing — close deletes only the in-memory summary. If the editor still has the conversation open, its next activity (markSessionActive: any prompt, any load with history) recreates the summary and it reappears within one heartbeat. A wrongly closed conversation recovers on its own; an editor-retired one stays gone. A bridge-restart auto-resume alone does NOT resurrect a closed session (verified by test) — real use does.

Untouched by close: backend session store, editor conversation storage, App tasks-index.

Changes

  • src/remote/session-close-endpoint.ts (new): bridge loopback POST /sessions/{id}/close — 404 unknown, 409 running, 200 {ok:true}.
  • src/remote/endpoint.ts: route wiring.
  • src/remote/hub-server.ts: POST /api/instances/{id}/sessions/{sid}/close forward-and-relay proxy. Bug found en route: keying the upstream abort on the inbound request's 'close' reset the bridge socket on every proxied close (empty-body POST drains and closes before the relayed response finishes writing → ECONNRESET → 502). The abort now keys on the response side closing early (res 'close' with writableEnded false).
  • Docs: REMOTE-CLIENTS "Closing a session" chapter, ARCHITECTURE summary, ADR-0006.

Test plan

  • typecheck / lint / full suite green (652 tests, +8)
  • bridge: retire idle session (discovery + status), 409 running, 404 unknown, 405 non-POST
  • self-heal: closed session reappears after editor activity; mere re-registration (editor restart auto-resume) does not resurrect it
  • hub proxy: POST relay incl. 409 passthrough, 401/404/502, non-POST falls to 404

- POST /api/instances/{id}/sessions/{sid}/close retires a conversation from
  remote discovery: the bridge deletes its in-memory summary (backend store,
  editor storage, tasks-index untouched — close, not delete)
- running turns are refused (409); editor-side-still-open conversations
  reappear on their next activity (hasActivity re-arm), so wrongly closed
  sessions self-heal while editor-retired ones stay gone
- hub POST proxy: abort upstream on the response side closing early, not the
  request's 'close' (empty-body POST drains first and reset every proxied
  close with ECONNRESET)
- docs: REMOTE-CLIENTS chapter, ARCHITECTURE summary, ADR-0006
@william0wang
william0wang merged commit f1f8b7c into main Aug 20, 2026
1 check passed
@william0wang
william0wang deleted the feat/remote-session-close branch August 20, 2026 05:01
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