Agents Manager: poll for the reply after a page change mid-turn - #113926
Agents Manager: poll for the reply after a page change mid-turn#113926AnnaMag wants to merge 6 commits into
Conversation
Navigating while the server is still answering leaves the panel with an unanswered user bubble: the transcript is fetched once on mount and nothing picks up the reply that lands afterwards. Refetch every 3s (up to 90s) while the last loaded message is a user turn and the merchant has not taken over the conversation in this tab, with a "Waiting for the reply…" indicator. (cherry picked from commit 7954118)
|
WordPress.com
Automattic for Agencies
|
Turns that already belong to a server session are reloaded by useConversation on mount, and the reply is now polled for separately (#113926). Keep only the local-* case: the first send of a session, orphaned before the server assigned an id, is marked failed and offered a retry.
|
Here is how your PR affects size of JS and CSS bundles shipped to the user's browser: Async-loaded Components (~359 bytes added 📈 [gzipped]) Details
React components that are loaded lazily, when a certain part of UI is displayed for the first time. Legend What is parsed and gzip size?Parsed Size: Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory. |
…script The server persists a user turn only once it starts processing it, so a transcript fetched right after a page change can end on the previous agent turn and hide that a question is in flight. Read the in-flight turns from the local conversation store and keep polling until the server shows a reply to each of them.
Always read the tab's pending turns once per session (the flag is applied when deciding to poll), drop the unreachable catch and unmount guard, and the redundant Reader Chat check.
Tool-heavy Woo turns (catalog review, batch edits) routinely exceed 90s.
loadConversation ran on every session, and a thrown read left the lag case (transcript still ending on the previous agent turn) with nothing to poll. Only read the local store when polling, and catch so a failed read still follows the server transcript.
There was a problem hiding this comment.
Great catch on the lost-reply case — definitely worth fixing! My concern is reliability: polling the hydration query replaces the whole conversation on every check, and the stop conditions can fail to terminate. Two repros:
- The composer is fully locked while waiting — Stop aborts nothing, Enter is blocked:
- An infinite re-fetch with the PR's own scenario (send a question → change the page very quickly): the send never reached the server, so the message disappears and the refetch runs every 3s until the 5-minute timeout — there's no give-up branch, so the question is simply lost (#113591's recovery doesn't cover this case either).
2026-08-31.7.17.22.mov
Could we rework it toward a more reliable approach? Claude's suggestion, using existing primitives:
- Snapshot the local
pendingturns once at mount. - Poll a cheap page-1 probe on its own query key; detect the reply by message count + newest turn being an agent reply — no text matching.
- On detection, invalidate the hydration query once (guarded by
isTurnInFlight()). - Past the deadline, mark the turn failed via the already-exported
reconcileWithServerinstead of losing it. - Show the waiting state via the notice slot, not
isProcessing, so the composer keeps working.
Any approach that avoids these failure modes works too — happy to pair on it, thanks! :)
Part of WOOAI-872, WOOAI-847
Proposed Changes
useConversation: newrefetchWhileAwaitingReplyoption. Refetch every 3s (up to 5 minutes — tool-heavy Woo turns run long) while a question is still unanswered on the server — the loaded transcript ends on a user turn, or a turn this tab sent (stillpendingin agenttic's local store) has no reply yet. The second signal matters: the server persists a turn only once it starts processing it, so a transcript fetched right after a page change can still end on the previous agent turn. ExposesisAwaitingReply.refetchIntervalInBackground: trueso a merchant who switches tabs while waiting still gets the reply.@automattic/agenttic-client: exportloadConversationandmessageTextContent(used to read the pending turns).OrchestratorChat: enable it for non-Reader agents unless the merchant has already sent a message in this tab or a send is in progress (so a refetch can never replace a live stream); show a "Waiting for the reply…" indicator meanwhile.Why are these changes being made?
When a merchant sends a message and the page changes before the reply comes back (own navigation, or the agent's
wp_admin__navigate), the next page fetches the conversation once on mount. If the server is still answering, that fetch returns the question with no reply — and nothing ever fetches again, so the panel shows an unanswered bubble until the next full page load. Merchants read this as "the assistant didn't answer".The turn did reach the server in this case, so no client-side recovery is needed — just keep fetching until the reply is there.
Testing Instructions
Sandbox (Simple/Atomic): sandbox
widgets.wp.com,cd apps/agents-manager && yarn dev --sync, enable Unified Chat under Automattician Options on/wp-admin/profile.php.odie/chat/...) and the reply appears without reloading, indicator gone.Calypso (
yarn start): same steps within the Calypso chat.Pre-merge Checklist
widgets.wp.com