The question
Send a chat message (typed or spoken), then navigate away before the reply lands. Does the turn
still complete?
Finding: a Loop is durable, a plain chat turn is not
Loops are safe. POST /loop writes an agent_loop_runs row and a server-side driver runs it.
InstanceDetail.tsx:292 states the design outright: "The console used to BE the loop… That died
with the tab and could not be budgeted, because the platform did not drive it. Now the workflow
drives and this only reports", and "the run itself is durable and carries on regardless of
whether this tab can see it." Correct, and exactly right.
A plain chat turn has none of that. AgentDO.handleChat awaits this.think(...) and returns
the reply inline. There is no ctx.waitUntil, no blockConcurrencyWhile, nowhere in
agent-do.ts — the whole turn hangs off the inbound request. Nothing keeps it alive if the
client goes away.
Why the failure is worse than "you lose the reply"
The assistant message is only appended after think() returns. But tools execute during it,
and their side effects are already committed — memory writes, created tasks, a filed GitHub issue,
a started pipeline, a start_work handoff.
So an interrupted turn can leave: work done, nothing in the transcript. Come back and the
conversation shows your message and no reply, while the agent has in fact acted. The next turn is
then built from a history that contradicts what happened — and the honesty rules in the system
prompt cannot help, because the record itself is wrong.
This is the same class the codebase already fixed once for partial failures: #24 added
partialToolLog so a late-round error still surfaces the side effects that landed. That guard runs
inside the catch. A cancelled request never reaches it.
Worth checking before fixing
Confirm empirically whether Cloudflare actually cancels the DO's in-flight work when the client
disconnects, or whether it runs to completion. The absence of waitUntil means it is unprotected
either way, but the fix differs: if it already completes, this is a documentation and
status-reporting matter (#252). If it is cancelled, the turn needs to survive the request.
Reproduce: send a message that triggers a slow tool round, close the tab immediately, then re-read
the thread via GET /v1/instances/:id/messages and the activity log. If activity shows tool calls
with no assistant message, it is cancelling.
If it is cancelling
Detach the turn from the request. The platform already has the pattern — a durable run row plus a
driver (agent_loop_runs, the Loop). A chat turn could take the same shape, or at minimum be
wrapped so the DO finishes and persists the reply even after the client is gone.
Verification
- Send → navigate away mid-turn → the assistant message is present on return.
- Tool side effects and the transcript never disagree.
The question
Send a chat message (typed or spoken), then navigate away before the reply lands. Does the turn
still complete?
Finding: a Loop is durable, a plain chat turn is not
Loops are safe.
POST /loopwrites anagent_loop_runsrow and a server-side driver runs it.InstanceDetail.tsx:292states the design outright: "The console used to BE the loop… That diedwith the tab and could not be budgeted, because the platform did not drive it. Now the workflow
drives and this only reports", and "the run itself is durable and carries on regardless of
whether this tab can see it." Correct, and exactly right.
A plain chat turn has none of that.
AgentDO.handleChatawaitsthis.think(...)and returnsthe reply inline. There is no
ctx.waitUntil, noblockConcurrencyWhile, nowhere inagent-do.ts— the whole turn hangs off the inbound request. Nothing keeps it alive if theclient goes away.
Why the failure is worse than "you lose the reply"
The assistant message is only appended after
think()returns. But tools execute during it,and their side effects are already committed — memory writes, created tasks, a filed GitHub issue,
a started pipeline, a
start_workhandoff.So an interrupted turn can leave: work done, nothing in the transcript. Come back and the
conversation shows your message and no reply, while the agent has in fact acted. The next turn is
then built from a history that contradicts what happened — and the honesty rules in the system
prompt cannot help, because the record itself is wrong.
This is the same class the codebase already fixed once for partial failures: #24 added
partialToolLogso a late-round error still surfaces the side effects that landed. That guard runsinside the
catch. A cancelled request never reaches it.Worth checking before fixing
Confirm empirically whether Cloudflare actually cancels the DO's in-flight work when the client
disconnects, or whether it runs to completion. The absence of
waitUntilmeans it is unprotectedeither way, but the fix differs: if it already completes, this is a documentation and
status-reporting matter (#252). If it is cancelled, the turn needs to survive the request.
Reproduce: send a message that triggers a slow tool round, close the tab immediately, then re-read
the thread via
GET /v1/instances/:id/messagesand the activity log. If activity shows tool callswith no assistant message, it is cancelling.
If it is cancelling
Detach the turn from the request. The platform already has the pattern — a durable run row plus a
driver (
agent_loop_runs, the Loop). A chat turn could take the same shape, or at minimum bewrapped so the DO finishes and persists the reply even after the client is gone.
Verification