Repository navigation
Flag turns that go silent as stalled instead of spinning forever - #273
Merged
Merged
Conversation
A turn whose task goes quiet inside kagent (a lost task state, a tool that never returns) showed a spinner for hours and nothing in Mainloop treated it as abnormal. The delivery loop now records when it last saw a live event of a delivered turn's task and what that event left it doing (thinking or writing, a tool call running, or parked for the owner). Writes are throttled, fenced on the claim and never touch updated_at, state or outcome_version. A sweep marks a turn stalled once it is quiet past SESSION_STALL_SECONDS (25m), or SESSION_STALL_TOOL_SECONDS (60m) while a tool runs; parked turns never stall. The next event clears it. The stall shows on NativeSessionInfo.stalled_turn and in the chat's turn recovery panel with stop and restart, in HealthView.stalled_turns and the Health page, and as an attention item for the main thread and standalone sessions. Nothing is replayed. Closes #265. Part of #262. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
oldsj
force-pushed
the
mainloop/stalled-turns
branch
from
October 18, 2026 22:26
1fc4578 to
19abbf9
Compare
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.
Stacked on #272 (restart). It targets that branch, so this diff shows only the stall watch. GitHub moves the base to
mainwhen #272 merges.On 2026-10-18 the main thread showed "Searching the web…" for over two hours, and four tasks were stuck the same way. Each turn's task had gone silent inside kagent, and nothing in Mainloop flagged it. This PR marks such turns as stalled, shows them with stop and restart, and raises attention and orchestration-health signals. The next task event clears the mark. Nothing is ever replayed.
What changes
Tracking (migration
00270, addsdeliveries.activity_at,activity_kindandstalled_at):event: thinking or writingtool: a tool call started with no result yetparked: waiting for input or authupdated_at,stateoroutcome_version.Marking stalled:
mark-stalled-turnssweep runs every 30 s. It marks a turn quiet for longer thanSESSION_STALL_SECONDS(1500), orSESSION_STALL_TOOL_SECONDS(3600) while a tool runs.session:messageso an open page refreshes.Where it shows (contract first):
NativeSessionInfo.stalled_turn(aTurnStall, or null) on/main-threadand/sessions/{id}/native.HealthView.stalled_turns: oldest first, narrowed by the task, session and attempt filters. The time window does not apply.turn_stalled, reasonstalled, links to the main thread or the session) for the main thread and standalone sessions. It is opened and resolved by the existing attention sweep. Task-owned sessions are skipped, because task liveness already blocks those tasks asstalledand opens a card.Web:
Specs updated:
docs/specs/orchestration-health.md(new "Stalled turns" section),docs/specs/chat.md,docs/specs/sessions.md.Verification
Checks run locally (macOS, using a disposable PostgreSQL 16 and a build overlay for the Linux-only
pushgate):go test -race ./...passes for every package exceptpushgate,workspacesgit-push tests (both need Linuxprlimit) and onedevloopunicode filename test (macOS normalizes names). These are environment-only failures in packages this PR does not touch; CI runs them on Linux.internal/sessions/stall_test.go:golangci-lint(v2.14.0,GOOS=linux): 0 issues.go generate ./...: no diff.make lint: no issues.tsc -bandeslint .are clean.vitestpasses forfeatures/chat,features/sessions,features/healthandfeatures/inbox.vite buildpasses.Not run: anything against a live kagent. This is fixture and fake proof only.
For the owner to check
WebSearchcalls started, no result), the main thread would have read as stalled about an hour after 16:13.Closes #265. Part of #262 (the turn-silence watchdog asked for in its comments).
🤖 Generated with Claude Code