fix(router,agent): defensive openedAt refresh + lifecycle hook error logging - #598
Open
rollroyces wants to merge 1 commit into
Open
rollroyces wants to merge 1 commit into
rollroyces wants to merge 1 commit into
Conversation
…logging
Two independent low-risk fixes:
1. router/health/ProviderHealthTracker.ts: refresh openedAt whenever
consecutive failures cross (or remain past) openThreshold, even when
state is already 'open'. The previous guard 'if state !== open' made
the assignment a no-op for that path, leaving openedAt stale from the
prior open cycle. A subsequent getState would then immediately
auto-promote back to half_open even though we had just observed
another failure. Also reset openedAt to 0 in recordSuccess when the
state recovers to healthy, so a stale timestamp cannot linger.
2. agent/loop/AgentLoop.ts: replace three silent '.catch(() => {})'
sites (PreModelRequest, InstructionsLoaded, onCompactPersisted) with
logged catches. Lifecycle hooks and the onCompactPersisted callback
are user-configured extensions; silently swallowing their failures
masks misconfigured hooks and lost writes. The remaining
'.catch(() => {})' sites in this file handle shutdown / cleanup
paths where logging would just be noise.
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.
Summary
Two independent low-risk fixes in two files that don't overlap with #589. Both ship with tests where applicable; build + test are green locally.
Changes
1.
src/router/health/ProviderHealthTracker.ts— defensiveopenedAtrefreshIn
recordFailure, whenconsecutiveFailures >= openThresholdand state is alreadyopen, the previous inner guardif (rec.state !== "open")made the assignment a no-op, leavingopenedAtstale from the prior open cycle. This means a buggy caller invokingrecordFailurewhile the circuit is stillopen(e.g. forgottenshouldSkipcheck) would push the consecutive-failure counter past the threshold but never refreshopenedAt. The nextgetStatecall would then immediately auto-promoteopen → half_open(becauseDate.now() - openedAt >= openDurationMs) even though we had just observed another failure. Under the canonical call path (shouldSkip → getState → request → recordFailure) this is harmless because the request only happens aftergetStatealready promoted tohalf_open, but the public class doesn't enforce that invariant.The fix refreshes
openedAt = Date.now()whenever the consecutive-failure counter crosses (or remains past)openThreshold, regardless of whether state was alreadyopen. Also resetsopenedAt = 0inrecordSuccesswhen state recovers tohealthy, so a stale timestamp cannot linger after a recovery.2.
src/agent/loop/AgentLoop.ts— logged catch on user-configured callbacksThree sites in
AgentLoop.tssilently swallow errors with.catch(() => {}):dispatchLifecycle(input, "PreModelRequest", ...)(line 538)dispatchLifecycle(input, "InstructionsLoaded", ...)(line 2046)input.onCompactPersisted({...})(line 2321)Lifecycle hooks and the
onCompactPersistedcallback are user-configured extensions — silently swallowing their failures masks misconfigured hooks and lost writes. Replaced with.catch((err) => console.warn(...))carrying session/turn context for diagnosability.The other
.catch(() => {})sites in this file (and across the codebase) are deliberately left alone — they handle shutdown / cleanup / file-unlink paths where logging would just be noise.CI / local verification
pnpm buildis green;pnpm testruns the entire suite:The upstream GitHub Actions CI matrix also runs cleanly against this branch:
static-checks(Desktop Smoke)docker(Web Regression)web(Web Regression,pnpm run build:web+ Vitest)windows-installer / buildThe Vitest Web suite that gates the merge reports 203 test files, 1734 tests, all passing against the PR head — my new specs are inside that count.
Both new tests pass:
The cancelled / skipped tests are pre-existing baseline noise unrelated to this PR.
Reviewers can reproduce locally with:
pnpm install --frozen-lockfile pnpm build pnpm testRisk
Low. Both changes are local:
ifblock restructured to always setopenedAt; one defensive reset added inrecordSuccess..catch(() => {})→.catch((err) => console.warn(...))substitutions, no behavior change on the success path.No public API changes, no behavior changes for canonical call paths, no schema/version bumps. The AgentLoop change adds logging only — the catch handler still swallows the error so the surrounding flow is unaffected.
Diffstat