Skip to content

Commit 8714a8e

Browse files
authored
fix(mothership): keep a message the server never admitted instead of dropping it (#8674)
* fix(mothership): keep a message the server never admitted instead of dropping it Two sends were lost without an error: - A send whose POST got no response (offline, Wi-Fi drop, waking a laptop) reconnected to the stream it would have opened. That stream does not exist, so the 404 read as "finished", the turn finalized as a success, and the refetched transcript no longer held the message. A queued follow-up was lost the same way, since it had already left the queue. - A send refused with 409 because another turn held the chat (started in another tab, or one this surface lost track of) reconnected to that turn under the new message's bubble, then vanished when it finished. Both now hand the message back under its id. An unreachable send is held in the queue, so it is not redispatched into the same failure, and goes out when the browser is back online (or when the user sends it). A send that found the chat busy waits in the queue behind that turn, which the chat shows as running, and goes out when it ends. Reusing the id keeps a retry deduplicated if the server did admit the first attempt. * fix(mothership): keep held and busy-refused sends exactly once across remounts - A first message held offline on the new-chat page sat under that mount's queue key, which dies with the mount, so a reload or remount before the network returned stranded it. Held sends on a chatless surface now carry the surface they belong to, and the next chatless mount of that surface adopts them. - Held sends are released for every chat when the browser comes back online, and on mount when it already is, so a send held in a chat the user is not viewing (or one whose `online` event fired with no surface mounted) still goes out. - A send refused because the chat is busy is handed back only after the chat's running turn has been read, so the queue cannot redispatch it before that turn ends. A busy refusal that does not name the running turn no longer reads as a deduplicated send, which reconnected to a stream that never existed and lost the message. * fix(mothership): send released held messages through the queue's own drain rules Releasing held sends on mount kicked the queue dispatcher directly, which skips the drain's guards. After a reload the chat history is not loaded yet, so a follow-up queued behind a still-running turn went out at once and was refused as busy. The release now only clears the hold; the drain effect, which waits for the history and for the running turn to end, sends a released head, and now also re-runs when the head's hold clears. * fix(mothership): don't hold a send whose network returned while it was failing An `online` event can fire while the failing POST is still pending, so the release ran before the message was held and the message then waited for a release that had already happened. A send now notes whether the browser came back online while it was in flight, and if so goes back to the queue unheld, for the drain to send under its usual rules. * chore(mothership): name sign-out in desktop tool lease docs and say "not run" once Follow-ups from the #8673 review: the lease docs now name sign-out (`stopAllDesktopTools`) alongside the user's Stop as what cancels a desktop tool, and the stale-observation message no longer says it was not run twice. * fix(mothership): restore a withdrawn queued send even after the user switched chats A queued send had already left the queue when its POST failed, and a dispatch whose epoch changed meanwhile (the user switched chats) skipped restoring it, so the message was lost. A withdrawn send was never admitted, so it now goes back to its own chat's queue regardless of the epoch. * fix(mothership): don't recreate a deleted chat's queue from a late restore A withdrawn send restored after its dispatch outlived a chat switch could land after the user deleted that chat, recreating a queue (and a message) for a conversation that no longer exists. Clearing a chat's queue now leaves a session tombstone that restores respect; a new enqueue for that key lifts it.
1 parent 662bd70 commit 8714a8e

7 files changed

Lines changed: 592 additions & 42 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/home/hooks/desktop-tool-lifetimes.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@ const runningTurns = new Map<string, RunningTurnTools>()
1717

1818
/** A running desktop tool's hold on its turn. */
1919
interface DesktopToolLease {
20-
/** Aborted only by the user's Stop of the turn. */
20+
/** Aborted only by the user's Stop of the turn, or by signing out. */
2121
signal: AbortSignal
2222
/** Called once when the tool settles. */
2323
release(): void
2424
}
2525

2626
/**
2727
* Starts a desktop tool (a browser action, a local file read or import) for a turn. Only the
28-
* user's Stop of that turn cancels it: replacing the stream reader, leaving the chat view, or
29-
* stopping another chat's turn leaves it running to finish and report its own result.
28+
* user's Stop of that turn, or signing out (`stopAllDesktopTools`), cancels it: replacing the
29+
* stream reader, leaving the chat view, or stopping another chat's turn leaves it running to
30+
* finish and report its own result.
3031
*/
3132
export function leaseDesktopTool(streamId: string): DesktopToolLease {
3233
let turn = runningTurns.get(streamId)

0 commit comments

Comments
 (0)