Skip to content

[Feature]: Add stale-progress watchdog for stalled agent sessions #4484

Description

@SoShymKing

Prerequisites

  • I will write this issue in English (see our Language Policy)
  • I have searched existing issues and discussions to avoid duplicates
  • This feature request is specific to oh-my-opencode (not OpenCode core)
  • I have read the documentation or asked an AI coding agent with this project's GitHub URL loaded and couldn't find the answer

Problem Description

OpenCode can sometimes keep an assistant/model turn alive without producing meaningful progress for a long time. This does not appear to be limited to background sub-agents. It can also happen in a main agent session or a normal conversation.

In my logs, I observed two related patterns:

  1. Main agent / normal session silent period

    In the main session, OpenCode started a model stream and then produced no message.part.updated event for a long time before the stream eventually resumed.

    Example pattern:

    service=session.processor session.id=<main-session> ... process
    service=llm providerID=openai modelID=gpt-5.5 session.id=<main-session> ... stream
    ...
    service=bus type=message.updated publishing
    # long gap
    service=bus type=message.part.updated publishing
    

    In one observed case, the gap between message.updated and the next message.part.updated was about 923 seconds. The session eventually resumed, so a short timeout would be too aggressive for main-agent turns.

  2. Background sub-agent stale state

    In a background sub-agent session, the first assistant step completed normally with tool calls. The child OpenCode session export showed:

    • first assistant step finished with finish: "tool-calls"
    • tool parts such as glob, grep, and glob all had state.status: "completed"
    • the step ended with step-finish

    After that, OpenCode created another assistant message, but that message had:

    • parts: []
    • all token counts set to 0
    • eventual error: MessageAbortedError: Aborted

    During that period, OMO continued to treat the background task as still running because the child session status remained busy.

    Example OMO log pattern:

    [background-agent] Session still running, relying on event-based progress:
    {"sessionStatus":"busy","toolCalls":9}
    

    The toolCalls count stayed fixed for the stale child task, while another background task continued making progress.

Eventually the stale child session was aborted, and OMO logged mixed lifecycle events such as:

Session error - no retry
Abort detected via session.error
Marked session cancelled
session.error received but session still alive, treating as transient
Task completed via session.idle event
Task cancelled via background_cancel

From a user perspective, this can look like the agent is still working, but there may be no actual progress. For background sub-agents, this is especially harmful because the parent/main workflow may keep waiting for the stale child result. For main-agent or normal conversation sessions, the same kind of silent period can make the UI appear stuck, even if it may eventually resume.

This feature request is not asking OMO to fix the underlying OpenCode/provider behavior that can create zero-part or long-silent assistant turns. Instead, it asks OMO to add a progress-aware watchdog so it can detect and handle agent sessions that are busy but not making meaningful progress.

Proposed Solution

Add a stale-progress watchdog for agent sessions, covering both:

  • background sub-agent sessions
  • main agent / normal conversation sessions

The watchdog should track last meaningful activity, not just elapsed time or sessionStatus.

Meaningful activity could include:

toolCalls count changed
message.part.updated received
message.part.delta received
message.updated with new parts/tokens
assistant part count changed
token count changed
background output became available
session status changed to a terminal state

If a session remains busy but none of the above progress signals change for a configured interval, OMO should classify the session as potentially stale and take a role-appropriate action.

  • Suggested behavior for background sub-agents:

    Background sub-agents can use more aggressive thresholds because they are usually delegated, bounded tasks and can be retried or reported to the parent.

    Suggested defaults:

    backgroundWarnNoProgressMs = 3 * 60_000
    backgroundRetryNoProgressMs = 8 * 60_000
    backgroundHardFailNoProgressMs = 15 * 60_000

    Suggested actions:

    1. After backgroundWarnNoProgressMs, log a clear stale-progress warning with task ID, session ID, agent, model, last tool count, and last activity timestamp.
    2. After backgroundRetryNoProgressMs, cancel the stale child and retry through the configured fallback chain if retry is available.
    3. After backgroundHardFailNoProgressMs, mark the child task as failed or cancelled and notify the parent session instead of leaving the parent waiting indefinitely.
    4. Treat zero-part / zero-token post-tool assistant turns as high-confidence stale candidates when they remain unchanged for several minutes.
    5. Ensure that a background task resolves through one terminal path only: completed, failed, retried, or cancelled. Avoid mixed lifecycle transitions such as idle-complete followed by background_cancel.
  • Suggested behavior for main agent / normal conversation sessions:

    Main sessions should use more conservative thresholds because long silent reasoning periods can still resume successfully.

    Suggested defaults:

    mainWarnNoProgressMs = 10 * 60_000
    mainSoftInterruptPromptMs = 20 * 60_000
    mainHardNoProgressMs = 30 * 60_000

    Suggested actions:

    1. After mainWarnNoProgressMs, show/log that the main session has had no visible progress for a long time.
    2. After mainSoftInterruptPromptMs, optionally surface a non-destructive warning or allow the user to cancel/retry.
    3. After mainHardNoProgressMs, classify the session as stalled only if there are still no activity signals.
    4. Avoid automatically killing main-agent sessions too early, because long silent main-agent turns can eventually resume.

Alternatives Considered

No response

Doctor Output (Optional)

Additional Context

  • The issue was a background-agent lifecycle / stale-progress problem.
  • A child session stayed busy with a fixed toolCalls value.
  • The child OpenCode session later showed a zero-part / zero-token assistant message ending with MessageAbortedError.

oh-my-opencode.log
2026-05-25T112230.log
child_session.json

Feature Type

New Hook

Contribution

  • I'm willing to submit a PR for this feature
  • I can help with testing
  • I can help with documentation

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions