Repository navigation
fix(threads): show tool calls a server restart killed as stopped - #532
Merged
Merged
Conversation
A command running when the server restarted never got a completion row, because its provider process died with the server. Both clients hide a tool row that is still in progress once its turn ends, so the command vanished from the thread while the fold still counted it. Startup reconciliation now records a stopped completion for every unfinished tool call in the turn the restart interrupted, and web and mobile show a stopped tool call with a "stopped" heading instead of hiding it.
incognitojam
force-pushed
the
styal/stop-tool-calls-killed-by-restart
branch
from
September 29, 2026 11:11
c4b6a36 to
ee0acee
Compare
incognitojam
enabled auto-merge (squash)
September 29, 2026 11:12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When the server restarts while an agent is running a command, the command's process dies with the server and never reports a result. Its row stayed "in progress" forever, and because both clients hide in-progress tool rows once a turn has ended, the command disappeared from the thread. The fold header still counted it ("1 terminal command"), but expanding the fold showed nothing.
What changed
tool.completedactivity with statusstoppedfor every tool call in that turn that has no completion. This happens whether the thread is then continued or settled with an error. Threads whose provider session is still live are left alone.listUnfinishedToolCalls, reads only the start and update rows of that turn's unfinished calls.stoppedis now shown rather than hidden as running, with a stopped heading: "Command stopped", "Edit stopped", and so on, matching "Command failed". The rule lives inworkEntryIsStoppedToolCallin client-runtime and thestoppedflag of the shared tool-row presentation. Background tasks that report cancelled or interrupted progress are unchanged and stay hidden.restart-stopped-tool-callsentry.No provider reports a
stoppedtool status today. The live database I checked has none, so the client change affects only the rows this fix records.Both shots show the same thread with the "Worked for" fold opened. The agent started a 60-second command, and the server restarted while the command was running:
Before, the command is missing even though the fold's header counts one terminal command. After, it shows as "Command stopped". The final reply is the agent's own report after it was continued.
Validation
node -e "setTimeout(…, 60000)"in the foreground, and I restarted the server while the command was running. On startup the server recorded onetool.completedrow with statusstoppedfor that call, on the interrupted turn and at the call's last update time, and then continued the thread. The web app shows "Command stopped - node -e …" inside the turn's fold, with no page errors. The screenshots above are from that thread; the before shot uses the same data with the client rule turned off, which is what current clients render.serverRuntimeStartup.reconcile.test.ts: an orphaned running thread gets exactly one stopped completion, for its open call, using the call's latest payload and time. A call whose last update already failed is skipped, and a thread with a live session is untouched. The test fails with the reconcile step disabled.ProjectionThreadActivities.test.ts: runs the query against SQLite. It returns only rows for that turn's calls that have notool.completed, and skips rows without a call ID.session-logic.test.ts(web) andthreadActivity.test.ts(mobile): a stopped tool completion stays visible, and the mobile row reads "Command stopped". Both fail with the shared rule disabled. The existing test that keeps cancelled background tasks hidden still passes.toolRowPresentation.test.tscovers the "Command stopped" heading.Not verified
Related upstream work: pingdotgg#9391 settles background agent tasks the same way when a session ends. It does not cover tool calls.
Written by an agent (Claude Code, claude-opus-5-5).