Skip to content

feat: re-send undecided interaction requests to reconnected remote clients - #64

Merged
william0wang merged 1 commit into
mainfrom
feat/reconnect-interaction-resend
Aug 20, 2026
Merged

feat: re-send undecided interaction requests to reconnected remote clients#64
william0wang merged 1 commit into
mainfrom
feat/reconnect-interaction-resend

Conversation

@william0wang

Copy link
Copy Markdown
Owner

Problem

session/request_permission / elicitation/create are one-shot requests raced across the clients connected at fire time (first response wins). A remote client that was offline when the agent asked (or dropped mid-wait) never sees the request; after reconnecting it still cannot answer — the reannounce dedup only records zcode ids (no re-prompt) and the session/load replay only re-sends pending tool_call cards, not interaction requests.

Fix

  • src/handlers/server-requests.ts: every interaction wait in requestWithTimeout is tracked as an ActiveInteraction — a cross-attempt first-response-wins race (method/params + deferred + per-attempt AbortControllers). The first attempt goes through the broadcast proxy as before; targeted re-sends join the same race and losers are aborted (SDK emits $/cancel_request, so Zed and the phone client dismiss their dialogs).
  • src/index.ts: after a client's session/load / session/resume completes (the reconnect catch-up), resendPendingInteractions re-sends that session's still-unanswered interaction requests to it, 300 ms delayed so the replay renders first. Connect-time resend was rejected deliberately: the phone client clears permission/elicitation state when a load starts, which would swallow an immediately re-sent dialog.
  • On wait settle (answered OR interrupted) the entry is unregistered and remaining attempts aborted — this also dismisses Zed's stale popup after a turn cancel (previous behavior left it open).
  • A re-send client whose request throws synchronously is funnelled into the rejection path: the resend fires from a timer callback, where a sync throw would crash the bridge.

Verification

  • 6 new tests in tests/interaction-resend.test.ts (reconnect adoption + loser cancel, session filtering, original-wins, interrupted-wait, already-answered, sync-throw survival).
  • pnpm typecheck / pnpm lint / full suite (42 files, 658 tests) pass.
  • Cross-checked the abort semantics against the ACP SDK source (abort only emits $/cancel_request; the request promise stays pending until the peer answers) and the phone client's onCancelRequest handling.
  • docs/PROTOCOL.md: documented the reconnect-resend behavior in the interaction routing section.

@william0wang
william0wang merged commit 7342c68 into main Aug 20, 2026
1 check passed
@william0wang
william0wang deleted the feat/reconnect-interaction-resend branch August 20, 2026 10:55
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