You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[bug] Navigating back loses 'working on it' — the run is still going and the API already reports it #252
Start work, navigate away, come back — "working on it" is gone. The agent may still be working; the
console simply stops claiming so. There is no way to tell a finished run from a running one.
It is entirely client-side, and the data already exists
The thinking indicator is local React state.pages/InstanceDetail.tsx:
:80 const [thinking, setThinking] = useState(false);
:327 setThinking(true); // on send
:352 setThinking(false); // when the reply lands
Never seeded from the server. Unmount discards it; remount starts at false.
The loop watcher only knows about loops this tab started.pollLoop opens with:
if(!id||!loopRunIdRef.current)return;
loopRunIdRef is set when you press Loop in that tab. After a remount it is null, so the
watcher returns immediately every tick — even with a run active.
But the server can answer this already:
Source
What it gives
GET /v1/instances/:id/loop (routes/tools.ts:457 → listLoopRuns)
every run for the instance, with status + iteration
GET /v1/instances/:id/loop/:runId (:460)
one run
WS broadcast
already emits {type:"status", status:"thinking"}
AgentDO state
records status: "thinking" (readable via /state)
Nothing needs building on the backend. The Assistant tab just never asks on mount.
And the one UI that does show runs is in the wrong place
LoopRunsSection — the component that lists loop runs — is rendered only in SettingsTab.tsx:1145. So the answer to "is my agent working?" lives in Settings, several clicks
from the Assistant tab where the work was started and where the user is looking.
Proposal
On mount, call GET /v1/instances/:id/loop; if a run is running, adopt its id into loopRunIdRef and resume the existing watcher. The polling machinery already works — it is only
missing its starting value.
Show a persistent indicator on the Assistant tab whenever a run is active, with the iteration
count that pollLoop already reads.
For a plain chat turn, seed the thinking state from the DO's own status rather than assuming
idle. Note ensureStateDefaults (agent-do-prompt.ts:63) resets thinking/error → idle
when the DO reloads, which is a sensible self-heal but means status is only trustworthy while
the object is warm — so treat it as a hint, and let [bug] A chat turn is bound to the request — navigating away can lose the reply while its tool side effects persist #251 decide what "in flight" should durably
mean for chat.
Consider surfacing LoopRunsSection (or a compact form of it) where work happens, not only in
Settings.
Verification
Start a loop, navigate to another agent and back: the indicator and iteration count return
without a page reload.
Two tabs open on the same instance both show the run — neither "owns" it.
A finished run clears on return rather than showing stale "working".
Verified true as written (line numbers had drifted ~+30, and it was worse than stated: the watcher effect at :509 also returns early on loopOn, which is likewise false after a remount — so it was doubly dead, not just returning every tick). Also confirmed: the console has no WebSocket client at all, so the WS {type:"status"} broadcast is not a drop-in; the poll path is.
Shipped:
lib/workInFlight.ts (pure + 11 unit tests): adoptableRun (newest running run — the real status vocabulary is running/completed/failed/needs_human/cancelled), shouldAdopt, isChatWorking.
checkWork in InstanceDetail polls /state + /loop every 10s (skipped while the tab is hidden), adopts a running run into loopRunIdRef/loopOn — the existing watcher only ever lacked its starting value — and restores objective, iteration and max, so the Stop button and its iteration badge come back on the Assistant tab, where the work was started.
A finished run clears on return rather than showing stale "working", and when a turn we didn't start completes, the transcript is refreshed.
An adopted run's completion notice is local-only (emitSystemChat(…, false)) — otherwise every tab watching the same run would write one into the transcript.
Not done, deliberately: moving LoopRunsSection out of Settings. The adopted run now shows where the work happens, which is the part that mattered; a second listing there can be its own change.
Activity
fix(api): a chat turn outlives the request that asked for it (#251)
fix(voice,console): stop losing a spoken turn (#175) and stop claimin…
serge-ivo commentedon Aug 6, 2026
Fixed in 534db6d.
Verified true as written (line numbers had drifted ~+30, and it was worse than stated: the watcher effect at
:509also returns early onloopOn, which is likewisefalseafter a remount — so it was doubly dead, not just returning every tick). Also confirmed: the console has no WebSocket client at all, so the WS{type:"status"}broadcast is not a drop-in; the poll path is.Shipped:
lib/workInFlight.ts(pure + 11 unit tests):adoptableRun(newestrunningrun — the real status vocabulary isrunning/completed/failed/needs_human/cancelled),shouldAdopt,isChatWorking.checkWorkin InstanceDetail polls/state+/loopevery 10s (skipped while the tab is hidden), adopts a running run intoloopRunIdRef/loopOn— the existing watcher only ever lacked its starting value — and restores objective, iteration and max, so the Stop button and its iteration badge come back on the Assistant tab, where the work was started.thinking || remoteWork. Per the ticket's own caveat,state.statusis NOT trusted (ensureStateDefaultsscrubs a stalethinkingon reload) — the answer comes from the DO's in-flight turn markers added in [bug] A chat turn is bound to the request — navigating away can lose the reply while its tool side effects persist #251.emitSystemChat(…, false)) — otherwise every tab watching the same run would write one into the transcript.Not done, deliberately: moving
LoopRunsSectionout of Settings. The adopted run now shows where the work happens, which is the part that mattered; a second listing there can be its own change.feat(stats): cards as data over a closed source vocabulary, and a rol…
refactor(console): four decisions out of the instance page, and the t…
fix(coder): the Coding tab's Loop stops being the loop, and starts on…
test(console): the touch half of the mute invariant, and the defect i…