Uh oh!
There was an error while loading. Please reload this page.
feat: detect stalled provider streams and auto-restart - #44677
Closed
wtnm wants to merge 8 commits into
Closed
Conversation
Fails a provider event stream with a typed LLMError (TransportReason kind IdleTimeout) when no element arrives within the configured window, using Stream.timeoutOrElse per-pull deadline instead of bare Stream.timeout which would silently end a stalled stream. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
MessageV2.fromError recognizes the watchdog's LLMError TransportReason kind IdleTimeout and maps it to a retryable APIError so SessionRetry.policy schedules another attempt; the stalled wording is also covered by the retryable message patterns. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Wraps both native and AI SDK event streams at the session LLM service boundary with the llm-package idle watchdog so a provider that stops sending tokens fails the stream instead of hanging. experimental.streamIdleTimeoutMs overrides the duration (0 disables); the field is declared on the opencode-side Info type pending the core schema carrying it. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Wraps the provider turn stream with the idle watchdog and retries a stalled attempt up to STREAM_IDLE_RETRIES times while nothing has been published for the assistant message; post-output stalls keep today's failAssistant behavior. Compaction transitions, interrupts, and tool-fiber settlement are preserved per attempt. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
A context-overflow providerError followed by a stall must route to compaction, not resend the identical oversized request twice more before recovery. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Contributor
The following comment was made by an LLM, it may be inaccurate: Found a potentially related PR: fix(provider): recover stalled model streams This PR appears related as it also addresses stalled model streams. Both PRs target the same problem domain of detecting and recovering from LLM provider streams that stop producing tokens. You may want to check if #40010 is still open or if it was superseded by this new implementation. |
6 tasks
Contributor
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
The watchdog previously counted silence from request start, false-tripping on long prompt processing before any token arrived. Now the deadline arms only after the stream begins delivering elements, and it is applied at the HTTP transport byte level so keep-alive frames and SSE comments reset it before framing drops them. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Three 800ms pauses under a 1s budget sum past it; only a single gap may trip the watchdog. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Silence between tool-call and its tool-result or tool-error is local execution time (bash, subagents, MCP calls, permission gates), and silence after step-finish is the next request transition; neither may trip the watchdog. The deadline now disarms on those boundary tags until the next element arrives, so only gaps inside continuous provider generation are bounded. Chunk-aware matching because pulls yield arrays. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
wtnm
commented
Aug 24, 2026
Author
Закрыт автором: требуется переоценка подхода после полевых проверок. |
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 freeto 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.
Issue for this PR
Closes #
No tracked issue. Related open PR on the same problem from the transport layer: #40010.
Type of change
What does this PR do?
A provider can return response headers and then stop sending tokens while keeping the connection open; both session paths waited forever in that state.
This adds an idle watchdog that fails the provider event stream when no event arrives within a window (default 120s). On the legacy path the failure surfaces as a retryable APIError, so the existing SessionRetry machinery retries it with backoff. In the V2 runner a stalled attempt restarts up to 2 times while nothing has been published for the assistant message; post-output stalls keep today's failAssistant behavior, and an overflow signal followed by a stall routes to compaction instead of resending.
The watchdog lives in
@opencode-ai/llmasguardIdle(Stream.timeoutOrElseper-pull deadline — bareStream.timeoutwould end stalled streams silently, looking like clean completion) and wraps both native and AI SDK runtimes at the session LLM boundary.experimental.streamIdleTimeoutMsoverrides the window,0disables it.How did you verify your code works?
bun typecheckin packages/llm, packages/opencode, packages/core.Screenshots / recordings
Not applicable — no UI changes.
Checklist