Skip to content

fix(remote): close the /acp session cleanly on disconnect + app exit - #57

Merged
brettchien merged 1 commit into
mainfrom
fix/remote-session-graceful-teardown
Aug 15, 2026
Merged

fix(remote): close the /acp session cleanly on disconnect + app exit#57
brettchien merged 1 commit into
mainfrom
fix/remote-session-graceful-teardown

Conversation

@brettchien

Copy link
Copy Markdown
Contributor

Problem

For the current build, neither closing the app nor clicking Disconnect releases the server-side /acp session:

  • disconnect() only did task.abort() — the WS socket is dropped with no Close frame, and by the resume-on-reconnect design the gateway keeps the session alive for a resume that never comes.
  • There was no app-teardown hook at all (.run(generate_context!()) with no RunEvent handler), so Cmd-Q / window close never even called disconnect().

Net: a quit/closed Studio leaves its session lingering on the gateway until session_ttl_hours=1 or the liveness reaper fires. With max_sessions=3, connect/quit churn can exhaust the slots.

Fix

  1. Graceful disconnectdisconnect() now signals the live loop (OutMsg::Shutdown) to abandon any in-flight turn (session/cancel) and send a WS Close frame before the task ends, so the gateway sees an intentional close (not a resumable blip) and frees the slot immediately. A shared stop flag makes run_reconnecting stop instead of reconnecting; a 400 ms flush window + abort() backstop bound the teardown.
  2. App-exit hook — builder switched to build() + run(|app, event| …); on RunEvent::ExitRequested it calls disconnect(), covering Cmd-Q / window close.

Verification

  • rustfmt --check parses both files clean; wiring reviewed.
  • Full compile is gated by CI (macOS) — this box can't build src-tauri (no GTK).
  • Suggested manual check once built: connect → quit the app → confirm the gateway session count drops promptly (not after the 1 h TTL); and Disconnect button → Activity log shows a clean close with no auto-reconnect.

🤖 Generated with Claude Code

Previously neither the Disconnect button nor app close released the
server-side session: `disconnect()` only `task.abort()`ed (dropping the
socket with no WS Close), and there was no app-teardown hook at all — so a
closed/quit Studio left its `/acp` session lingering on the gateway until
its TTL (session_ttl_hours=1) or liveness reaper fired. With max_sessions=3
that can exhaust the slots across connect/quit churn.

1. Graceful disconnect: `disconnect()` now signals the live loop
   (OutMsg::Shutdown) to abandon any in-flight turn (session/cancel) and
   send a WS Close frame before the task ends, so the gateway sees an
   intentional close (not a resumable blip) and frees the slot immediately.
   A shared `stop` flag makes run_reconnecting stop instead of reconnecting;
   a 400ms flush window + abort backstop bound the teardown.

2. App-exit hook: switch the tauri builder to build()+run() and call
   disconnect() on RunEvent::ExitRequested, covering Cmd-Q / window close.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@brettchien
brettchien merged commit 82fdbab into main Aug 15, 2026
2 checks passed
@brettchien
brettchien deleted the fix/remote-session-graceful-teardown branch August 15, 2026 08:19
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