feat: remote session close via HTTP with running guard and self-healing - #61
Merged
Conversation
- 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
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 loopbackPOST /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}/closeforward-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'withwritableEndedfalse).Test plan