Commit Graph

5 Commits

Author SHA1 Message Date
YeonGyu-Kim 20efb79fbf test(runtime-fallback,prompt-gate): add red tests for event-shape and finish-marker blind spots (BUG-C+D)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 13:12:06 +09:00
YeonGyu-Kim 44ef5dec1d fix(runtime-events): honor OpenCode progress shapes 2026-05-19 13:48:45 +09:00
YeonGyu-Kim 9ddf13107d fix(test/runtime-fallback): add git_master to config fixture
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-18 16:53:13 +09:00
Ivan Smetanin 3199bd3d90 fix(runtime-fallback): broaden watchdog progress detection + harden test timing
Addresses two issues identified by cubic on PR #3952.

1. Watchdog cancellation was too narrow — only `text`/`reasoning` parts
   counted as progress, so a subagent that immediately ran tools
   (Read/Bash/Edit) emitted `tool`/`tool_use`/`tool_result`/`tool-call`/
   `step-start` parts that the watchdog ignored, risking a false fire
   on actively-working subagents. Broaden to: any assistant part of any
   known type counts as progress (the model has started responding,
   whether or not visible text has arrived yet). `info.error` and
   `info.finish` continue to cancel.

2. Test timing margins were tight (15ms pre-cancel against a 40ms
   timer), risking CI flakiness on loaded runners. Bumped to a 100ms
   threshold with a 40ms pre-cancel window and a 250ms post-fire wait,
   giving a 60ms margin before the timer fires and ~2.5x the threshold
   after — robust against scheduler delay.

Refactor for testability: extracted the OpenCode-event→watchdog-signal
translation out of `hook.ts` into an exported `observeEventForWatchdog`
helper on the watchdog module. This let me add direct unit tests for
every part-type case (text, reasoning, tool, tool_use, tool_result,
tool-call, step-start, file) plus the error/finish/empty-parts branches
without spinning up the full hook. Net diff: hook.ts shrinks, watchdog
module gains a small pure function with parametrised coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 20:47:03 +09:00
Ivan Smetanin a130fa70d1 fix(runtime-fallback): add first-prompt watchdog for stuck subagents
When a subagent is dispatched to a provider and the underlying SDK
enters a silent internal retry loop on a 429/quota error, no error
event is ever emitted back to OpenCode. The runtime-fallback hook —
which is fully reactive (listens to message.updated/session.error/
session.status) — has nothing to react to and never dispatches the
configured fallback. The subagent sits in `retry` status until the
parent's 30-minute poll timeout (DEFAULT_POLL_TIMEOUT_MS) gives up,
during which the parent's pending task tool call shows "waiting for
subagent" with no indication of failure.

This change adds a first-prompt watchdog that synthesises the missing
error-event trigger:

  - Armed when a user message lands in a subagent session
    (membership check via `subagentSessions`).
  - Cancelled on the first sign of progress: any assistant message
    with text/reasoning content, finish field, or an error field (any
    of which is something the existing handlers will deal with).
  - Cancelled on session terminal events (idle/stop/deleted/error).
  - On fire (90s default): aborts the in-flight request and routes
    into the existing dispatchFallbackRetry path — the same code that
    runs when a session.error arrives. No new fallback mechanism.

Design choices:

  - Dispatch fallback, do not abort the subagent outright. Network
    loss looks identical to a stuck retry from the hook's vantage
    point; with fallback-dispatch behaviour, network loss degrades
    to today's baseline (both attempts fail, 30-min outer timeout
    still ends things) rather than destructively aborting work.
  - Scope strictly to subagents. Parent/user sessions can legitimately
    take 90s+ to produce the first token; subagent dispatches in
    practice produce first content much faster, so a 90s ceiling is
    safe.
  - Threshold is tunable via the third arg to createFirstPromptWatchdog;
    DEFAULT_FIRST_PROMPT_WATCHDOG_MS = 90_000 in constants.ts.

Also adds a diagnostic log in session-status-handler when a
`session.status: retry` event arrives whose message does not match
RETRYABLE_ERROR_PATTERNS. This is the hook's other silent-return
spot for retry events; logging the raw retry message will let us
extend the patterns next time we hit a provider whose phrasing
we don't yet match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 20:45:46 +09:00