Update Dispatcher ClientIO interactions #2183
Merged
GeorgeNgMsft merged 18 commits intomainfrom Apr 10, 2026
Merged
Conversation
Proposes converting the blocking askYesNo, proposeAction, and popupQuestion callbacks to a non-blocking deferred-promise pattern modeled on the existing requestChoice/respondToChoice mechanism. Covers protocol changes, DisplayLog schema extensions, pending interaction management, replay/reconnection strategy, and a phased implementation plan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cks (Phases 1-8) Convert askYesNo, proposeAction, and popupQuestion from blocking RPC callbacks to a non-blocking deferred-promise pattern with correlation IDs, enabling multi-client broadcast and resilient disconnect handling. - Define PendingInteractionRequest/Response types and discriminated union for askYesNo, proposeAction, popupQuestion interaction types - Add PendingInteractionManager with create/resolve/cancel lifecycle, per-connection cleanup, and timeout support - Extend DisplayLog with logPendingInteraction and logInteractionResolved for interaction persistence and replay - Add requestInteraction/interactionResolved fire-and-forget methods to ClientIO interface and wire through RPC client/server layers - Add respondToInteraction to Dispatcher interface with RPC support - Rewrite SharedDispatcher to use deferred promises instead of blocking callbacks, with broadcast to all clients and first-responder resolution - Add requestInteraction/interactionResolved stubs to all ClientIO implementations (shell, CLI, browser extension, console, MCP, tests) - Add @typeagent/dispatcher-types dependency to agent-server and protocol Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…b.com/microsoft/TypeAgent into dev/georgeng/async-clientio-callbacks
…cancellation notification
…b.com/microsoft/TypeAgent into dev/georgeng/async-clientio-callbacks
Resolved conflicts in favour of explicit cancelInteraction approach over the auto-cancel-on-disconnect (cancelByConnection) approach from #2178. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the agent-server deferred ClientIO interaction flow to avoid implicitly cancelling pending interactions on client disconnect, introducing an explicit client-driven cancellation API and updating interaction queuing/logging behavior for replay.
Changes:
- Adds
Dispatcher.cancelInteraction(interactionId)to dispatcher types and RPC client/server plumbing. - Removes connection-scoped tracking/cancellation from
PendingInteractionManager(dropsconnectionIdandcancelByConnection). - Updates agent-server
SharedDispatcherinteraction handling and refreshes related tests/docs.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| ts/packages/dispatcher/types/src/dispatcher.ts | Adds cancelInteraction() to the public Dispatcher interface. |
| ts/packages/dispatcher/rpc/src/dispatcherTypes.ts | Extends dispatcher RPC invoke contract with cancelInteraction. |
| ts/packages/dispatcher/rpc/src/dispatcherServer.ts | Wires cancelInteraction through the RPC server handler. |
| ts/packages/dispatcher/rpc/src/dispatcherClient.ts | Adds RPC client method for cancelInteraction. |
| ts/packages/dispatcher/dispatcher/src/dispatcher.ts | Adds base dispatcher stub that throws for cancelInteraction. |
| ts/packages/dispatcher/dispatcher/src/context/pendingInteractionManager.ts | Removes connection-based ownership and cancelByConnection; simplifies create(). |
| ts/packages/dispatcher/dispatcher/test/pendingInteractionManager.spec.ts | Updates tests for new create() signature and removes cancelByConnection coverage. |
| ts/packages/agentServer/server/src/sharedDispatcher.ts | Removes disconnect-driven interaction cancellation; adds explicit cancelInteraction behavior and adjusts queuing/logging behavior. |
| ts/packages/agentServer/docs/async-clientio-design.md | Updates the design doc to describe explicit cancellation and always-log behavior. |
Comments suppressed due to low confidence (1)
ts/packages/agentServer/docs/async-clientio-design.md:82
- The doc says reconnecting clients can respond to pending interactions, but later states JoinSessionResult.pendingInteractions mirrors broadcast eligibility based on requestId.connectionId. Since connectionId is server-assigned per connection and changes on reconnect, those two statements conflict. Clarify the intended routing identity (stable client id vs connection id) and update either the implementation or this doc section accordingly.
Disconnecting a client does **not** automatically cancel pending interactions. Interactions remain pending until they time out or a client explicitly calls `cancelInteraction`. This allows a reconnecting client to respond to the same interaction within the timeout window.
Clients that want to cancel an interaction (e.g., on user dismissal) call `dispatcher.cancelInteraction(interactionId)`, which triggers:
1. `interactionCancelled` broadcast to all clients (so they can dismiss stale UI)
2. An `interaction-cancelled` entry in the `DisplayLog`
### DisplayLog Integration
Three entry types are logged to `DisplayLog`:
- `pending-interaction` — uses `interaction.timestamp` (the creation time of the request, not wall-clock at log time) to keep replay order consistent
- `interaction-resolved` — response is JSON-safe normalized before storage
- `interaction-cancelled`
### Session Join / Reconnection
`JoinSessionResult` includes `pendingInteractions: PendingInteractionRequest[]`, populated by `getPendingInteractions(connectionId, filter)`. This mirrors the `broadcast` eligibility rules exactly — a joining client only receives interactions it would have been sent during normal operation.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… ClientIO action patterns
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.
This PR addresses comments in #2178
Changes :
Explicit client-initiated cancellation (cancelInteraction) instead of auto-cancelling interactions on client disconnect.
PR #2178 (already in main) implemented the async deferred-promise pattern with cancelByConnection — when a client disconnects, all its pending interactions are immediately cancelled. This branch replaces that with: