fix(agents): keep finite LLM idle watchdog under unlimited run budgets#101986
fix(agents): keep finite LLM idle watchdog under unlimited run budgets#101986obviyus wants to merge 5 commits into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 8, 2026, 8:21 AM ET / 12:21 UTC. Summary PR surface: Source +54, Tests +201, Docs 0. Total +255 across 8 files. Reproducibility: yes. source-reproducible. Current main and v2026.6.11 still show the no-timeout sentinel disabling idle watchdog behavior and the schema rejecting Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Keep this PR as the Track B3 review record, land the identical changes through the atomic integration PR after maintainer soak/acceptance, then close this branch from that merged path. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible. Current main and v2026.6.11 still show the no-timeout sentinel disabling idle watchdog behavior and the schema rejecting Is this the best way to solve the issue? Yes for this per-track branch. The resolver/config boundary is the right fix location and the prior explicit self-hosted run-cap gap is covered; the safest merge vehicle is the atomic integration PR rather than direct landing. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 4bf70be01a21. Label changesLabel justifications:
Evidence reviewedPR surface: Source +54, Tests +201, Docs 0. Total +255 across 8 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (7 earlier review cycles)
|
Live E2E validation — and two gaps it caught (both fixed in 5cbfd99)Re-ran the #101910 wedge harness (black-hole model server, scratch gateway profile) against this branch with the previously-lethal config: Results
Fixes (second commit,
|
|
Docs finding fixed in f677617: the cron sentence now distinguishes genuinely-local endpoints (loopback/private baseUrl, idle opt-out unchanged) from self-hosted providers on network baseUrls (300s implicit watchdog), and states the explicit-cron-timeout cap. That was the only code/docs-level finding; the remaining item is the maintainer decision on the config-zero + self-hosted-default semantics, which will be recorded here explicitly. |
|
Maintainer decision (@obviyus): accept both semantics changes.
Docs finding fixed in f677617. No remaining blockers from this review. |
|
Landing plan update: this track lands atomically with its siblings via the integration PR #102160 (all commits preserved, rebase merge) — sequential landing would put untested intermediate states on main (e.g. the stale takeover without the quiet-tool floor). This PR stays open as the per-track review record and closes when #102160 lands. |
What Problem This Solves
Track B3 of #101863. Three coupled timeout-semantics defects, all verified in source and one proven live (the wedge in #101910's E2E was built on exactly this coupling):
runTimeoutMs >= MAX_TIMER_TIMEOUT_MS) maderesolveLlmIdleTimeoutMsreturn0— disabling mid-stream stall detection entirely. Users who configure "no timeout" for long tasks silently trade away all liveness: one TCP half-open or provider stall wedges the turn forever.agents.defaults.timeoutSeconds: 0clamped to 1 second (Math.max(seconds, 1)), while the per-run override path defines0as unlimited — the same key with opposite semantics depending on position.Why This Change Was Made
0: an unlimited budget bounds total cost, not liveness. Explicitmodels.providers.<id>.timeoutSecondsstill wins and may exceed the defaults; genuinely local baseUrls keep their existing idle opt-out (prompt-eval silence is normal there).SELF_HOSTED_LLM_IDLE_TIMEOUT_MS = 300_000aligns self-hosted mid-stream tolerance with the existing 300s first-event allowance. Cloud stays 120s; cron clamps unchanged.timeoutSeconds: 0now resolves to the same unlimited sentinel as the override path. Safe because of the first change: unlimited budget no longer implies zero stall detection. The prior behavior (0 silently becoming a 1s budget that kills every turn) is a footgun, not a contract anyone can be relying on.docs/concepts/agent-loop.mdtimeout table updated (unlimited-budget semantics, self-hosted idle default).User Impact
timeoutSeconds: 0) becomes safe by default: unlimited run budget with a finite per-class stream watchdog — the exact hang mode reported as "agent hangs mid-task forever" can no longer be configured accidentally.Evidence
main(sentinel × explicit/implicit budget × cloud/self-hosted/local × cron) — precedence identical except the three intended changes.timeoutSecondsconfig table (0 → unlimited, finite, negative, NaN). Composition test: config0+ cloud provider = unlimited budget AND 120s idle watchdog.tsgocore/core-test lanes, oxlint/oxfmt — green on Testbox.Part of #101863 (Track B3). Fixes #85826; closes the
timeoutSeconds: 0footgun identified in the #101863 audit (root cause 2).