| 1 | // The remote running-state watchdog: while the pill claims a turn is running, |
| 2 | // poll the serve's /status and feed it through the shared backend_status |
| 3 | // reducer. This is the remote twin of the local tab's reconcile loop — a lost |
| 4 | // turn_done frame (dropped SSE, slow-consumer drop, half-dead tunnel) then |
| 5 | // clears within one tick instead of spinning forever. |
| 6 | |
| 7 | import { useEffect, type RefObject } from "react"; |
| 8 | |
| 9 | const REMOTE_RUNNING_RECONCILE_MS = 30_000; |
| 10 | |
| 11 | /** The connection effect's /status reader, absent while no connection owns the tab. */ |
| 12 | export interface RemoteStatusRefreshHandle { |
| 13 | tabId: string; |
| 14 | run(): Promise<void>; |
| 15 | } |
| 16 | |
| 17 | /** |
| 18 | * Polls /status while the shown tab claims a running turn and the runtime |
| 19 | * projection holds no liveness of its own, which is the only case the feed |
| 20 | * cannot settle by itself. |
| 21 | */ |
| 22 | export function useRemoteRunningWatchdog(input: { |
| 23 | tabId: string | undefined; |
| 24 | /** The shown tab adopted a session and hydrated it. */ |
| 25 | ready: boolean; |
| 26 | /** The runtime projection knows the turn, so its own liveness already settles the pill. */ |
| 27 | runtimeKnown: boolean; |
| 28 | running: boolean; |
| 29 | refreshStatusRef: RefObject<RemoteStatusRefreshHandle | null>; |
| 30 | }): void { |
| 31 | const { tabId, ready, runtimeKnown, running, refreshStatusRef } = input; |
| 32 | useEffect(() => { |
| 33 | if (!ready || runtimeKnown || !tabId || !running) return; |
| 34 | const reconcile = () => { |
| 35 | const current = refreshStatusRef.current; |
| 36 | if (!current || current.tabId !== tabId) return; |
| 37 | void current.run().catch(() => { |
| 38 | // Transient; the next tick retries. |
| 39 | }); |
| 40 | }; |
| 41 | const timer = window.setInterval(reconcile, REMOTE_RUNNING_RECONCILE_MS); |
| 42 | return () => { |
| 43 | window.clearInterval(timer); |
| 44 | }; |
| 45 | }, [ready, refreshStatusRef, running, runtimeKnown, tabId]); |
| 46 | } |
| 47 |