Skip to content

feat(providers): verify-only health sweep for web-cookie connections (#11488) - #11495

Merged
diegosouzapw merged 3 commits into
diegosouzapw:release/v3.8.51from
oyi77:feat/webcookie-health-probe
Aug 26, 2026
Merged

diegosouzapw merged 3 commits into
diegosouzapw:release/v3.8.51from
oyi77:feat/webcookie-health-probe

Conversation

@oyi77

@oyi77 oyi77 commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

Closes #11488.

What

The token health-check scheduler only iterated auth_type = 'oauth' rows, and its no-refresh-token branch no-opped anyway (tokenHealthCheck.ts :649/:770) — so every web-cookie connection (chatgpt-web, claude-web, grok-web, qwen-web, gemini-web, …) stayed testStatus: "active" until a live user request failed with 401/403.

This PR adds a verify-only path to the existing sweep:

  • sweep() now also fetches { authType: "cookie" } rows.
  • New leaf src/lib/tokenHealthCheckWebCookie.ts (same injectable-DI pattern as tokenHealthCheckKimi.ts / tokenHealthCheckCursor.ts) probes catalogued cookie providers via validateWebCookieProvider — the generic session-ping the dashboard Test button falls back to, proxy-fallback transport included.
  • Result mapping is deliberately conservative:
    • valid → silent lastHealthCheckAt stamp (no per-tick log noise);
    • unambiguous AUTH_007 / SESSION_EXPIRED → terminal expired with errorCode: session_expired, lastErrorSource: webcookie;
    • network / guard-block / 5xx / unsupported → stamp only, never a state flip.
  • Per-connection healthCheckInterval honored (default 60 min, 0 = off). kimi-web keeps its bespoke refresh leaf with precedence; no behavior change for OAuth rows.

The richer per-provider specialty validators are intentionally not used here: a background sweep that can terminal-state a connection must act only on an unambiguous signal.

Verification

  • New unit suite tests/unit/token-health-check-webcookie.test.ts: 9/9 pass (npx tsx --test --test-force-exit tests/unit/token-health-check-webcookie.test.ts) covering all branches — candidate detection, non-cookie passthrough, valid-stamp, expired-flip field mapping, unsupported/inconclusive stamp-only, interval gate, missing credential, providerSpecificData.cookie fallback.
  • Regression: tests/unit/token-health-check-kimi.test.ts 4/4 pass.
  • npx tsc --noEmit -p tsconfig.json: zero diagnostics in the three touched files.
  • npm run check:file-size: OK.
  • ESLint: both new files clean. One pre-existing error remains in src/lib/tokenHealthCheck.ts (@/lib/localDb restricted-import barrel) — reproduced on the pristine base commit before my change, untouched here.

Changed files

  • src/lib/tokenHealthCheckWebCookie.ts (new)
  • src/lib/tokenHealthCheck.ts (+20 wiring lines)
  • tests/unit/token-health-check-webcookie.test.ts (new)

…iegosouzapw#11488)

The token health-check scheduler iterated only auth_type='oauth' rows, so
web-cookie connections kept testStatus='active' forever and a dead cookie
surfaced only when a live request failed with 401/403.

- sweep() now includes auth_type='cookie' rows
- new leaf tokenHealthCheckWebCookie.ts probes catalogued cookie providers
  via validateWebCookieProvider (same generic session-ping as the dashboard
  Test button, proxy-fallback transport included)
- only an unambiguous AUTH_007/SESSION_EXPIRED flips the row to terminal
  expired (errorCode session_expired); network/guard/5xx ambiguity stamps
  the tick and changes nothing
- per-connection healthCheckInterval honored (default 60m, 0=off)
- kimi-web keeps its bespoke refresh leaf, dispatched before this probe
@oyi77
oyi77 requested a review from diegosouzapw as a code owner August 25, 2026 10:22
@oyi77

oyi77 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

CI triage for the current red batch (Docs Gates / unit shards / Vitest / ESLint-suppressions): reproduced at today's merge ref (a179ffed5 + this branch) with none of this branch's content — check:docs-counts fails STRICT on README/AGENTS/llm.txt still saying "159 migrations" while code has 160, and tests/unit/agent-card-route.test.ts + tests/unit/providers-constants-split.test.ts fail at the tip itself. Inherited tip-drift, not introduced here.

This branch's own receipts stay green: token-health-check-webcookie.test.ts 9/9, Kimi health-check regression 4/4, tsc clean on touched files.

@diegosouzapw

Copy link
Copy Markdown
Owner

Held out of this merge batch — found a real defect while validating (reproduced 3/3 in isolation, not present on the pure release/v3.8.51 tip since this is entirely new code):

`honors the interval gate — recently checked rows are skipped silently` fails with `probed === true` (expected `false`).

Root cause: `checkWebCookieConnectionIfNeeded` (`src/lib/tokenHealthCheckWebCookie.ts:85`) gates on `Date.now() - lastCheckMs < intervalMin * 60 * 1000` — using the real wall clock — while the test fixes `NOW = "2026-08-25T12:00:00.000Z"` and derives `lastHealthCheckAt` as 5 minutes before that constant. Whenever the suite runs more than `intervalMin` away from that hardcoded timestamp (verified against real UTC just now: `2026-08-25T20:42:45Z`, an 8h42m gap vs the 60-minute interval under test), the gate never trips and the test fails — a timebomb, same class as the GLM weekly-reset test #11450 just de-timebombed.

Two ways to close it, your call:

  1. Thread the injected `params.now` through the gate comparison instead of `Date.now()` — makes the function fully deterministic/testable and is probably the more correct shape given the function already takes `now` as a sweep-consistency timestamp.
  2. Keep `Date.now()` in the source (arguably fine for a real background sweep) and de-timebomb the test by computing `lastHealthCheckAt` relative to `Date.now()` instead of the fixed `NOW` constant.

Everything else in the PR looks solid — 8/9 in this file, the rest of the suite (expired-flip mapping, unsupported/inconclusive stamp-only, missing-credential, providerSpecificData.cookie fallback, kimi-web regression) passes clean. Happy to take another look once this is fixed.

diegosouzapw pushed a commit that referenced this pull request Aug 25, 2026
…amilies (#10788) (#11409)

Validated in a combined 3-PR batch worktree off release/v3.8.51 tip (a sibling PR from the same author, #11495, was held out after an interaction-only typecheck error was isolated to it — reproduced clean without it, see #11495's own comment).
- Focused tests: opencode-go-effort-aliases-6922.test.ts, opencode-go-effort-aliases-8353.test.ts, chatcore-upstream-body.test.ts — part of batch's 94/94 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Thanks for tracing this all the way to the wire format — forwarding the aliased id verbatim instead of injecting a field the non-DeepSeek families never had is exactly the right fix.
diegosouzapw pushed a commit that referenced this pull request Aug 25, 2026
…11497) (#11505)

Validated in a combined 3-PR batch worktree off release/v3.8.51 tip (a sibling PR from the same author, #11495, was held out — see its own comment for the isolated finding, unrelated to this diff).
- Focused test: web-cookie-expiry.test.ts — part of batch's 94/94 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Thanks for closing a real trust gap — operators deserve to know a cookie is about to expire before a live request fails.
diegosouzapw pushed a commit that referenced this pull request Aug 25, 2026
…s + construction to first use (#11220) (#11421)

Validated in a combined 3-PR batch worktree off release/v3.8.51 tip (a sibling PR from the same author, #11495, was held out — a typecheck error in zai-web.ts only reproduced with this PR + #11495 boarded together, and cleared without #11495; isolated this PR alone confirmed clean on its own too, so the interaction belonged to #11495's side — see its comment).
- Golden lock: executor-map-golden.test.ts — passes byte-identical (same keys, classes, provider identities, dispatch guards)
- Focused tests part of batch's 94/94 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Thanks for the measured, careful methodology here — the golden-lock contract plus the isolated DATA_DIR benchmarking make this an easy PR to trust despite the wide surface (72 files).
…rval gate

Gate compared lastHealthCheckAt against Date.now() while the function
already accepts an injected 'now' for stamping, making the interval gate
non-deterministic and the gate test a wall-clock timebomb (flagged by
maintainer on diegosouzapw#11495: fails whenever the suite runs >intervalMin away
from the test's hardcoded NOW constant).

Production behavior is unchanged — the sweep caller passes a fresh
new Date().toISOString() as 'now' — but the function is now fully
deterministic under injected time.
@oyi77

oyi77 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Fixed — took your option 1: the interval gate now compares against the injected now instead of the wall clock (8c1fd1ce3, single-line change):

if (lastCheckMs > 0 && new Date(now).getTime() - lastCheckMs < intervalMin * 60 * 1000) return true;

Production semantics are unchanged — the sweep caller (tokenHealthCheck.ts:635) already passes a fresh new Date().toISOString() as now — but checkWebCookieConnectionIfNeeded is now fully deterministic under injected time, which is the right shape for a function that already takes now for stamping. (The committed diff also carries a few prettier line-wraps from the repo's pre-commit hook on pre-existing lines in this file — no behavior change.)

Receipts on 8c1fd1ce3:

  • tests/unit/token-health-check-webcookie.test.ts: 9/9 pass, including honors the interval gate — recently checked rows are skipped silently. Determinism proof is inherent: that test's fixed NOW (2026-08-25T12:00Z) is >30h from real wall clock at run time, and the gate now trips correctly regardless.
  • Sibling regressions: token-health-check-kimi.test.ts + token-health-check-sweep.test.ts → 7/7 pass.
  • ESLint clean on the touched file; full-project tsc --noEmit produces zero diagnostics referencing tokenHealthCheckWebCookie.ts (the advisory Build check's error mass is pre-existing and unrelated).

Ready for re-review.

…gate

Complement to the recently-checked skip case: asserts a row checked
61 minutes ago against a 60-minute interval IS probed exactly once and
re-stamped. Locks both boundaries of the now-deterministic gate.
@oyi77

oyi77 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up covering the repo's PR gates (copilot-instructions.md):

Repro test — your reported defect was encoded as a test (the failing honors the interval gate case), and the fix makes it pass deterministically. Added its complement in 4cf955ce8: stale rows are re-probed once the interval has elapsed (checked 61 min ago vs 60-min interval → probed exactly once, re-stamped) so both boundaries of the now-deterministic gate are locked.

Coverage gate (npm run test:coverage, full suite, 34,377 tests) — --check-coverage thresholds all pass globally:

Metric Result Floor
Statements 76.18% 60%
Branches 79.09% 60%
Functions 71.03% 60%
Lines 76.18% 60%

Touched leaf module (src/lib/tokenHealthCheckWebCookie.ts, isolated c8 over its suite): statements 100%, functions 100%, lines 100%, branches 82.75%.

The npm script exits 1 solely because of 28 suite failures that pre-date this branch's content (identical count across two independent full runs, before and after the new test; zero involve tokenHealthCheckWebCookie or the sweep wiring — those pass 10/10 and kimi+sweep 7/7). Same inherited-tip-drift class triaged in my earlier comment. Happy to produce a named-failure ledger if useful, but it needs another ~35-min instrumented run, so flagging rather than assuming you want it.

Branch tip: 4cf955ce8 (fix 8c1fd1ce3 + test 4cf955ce8).

@diegosouzapw
diegosouzapw merged commit d351960 into diegosouzapw:release/v3.8.51 Aug 26, 2026
9 of 16 checks passed
diegosouzapw added a commit that referenced this pull request Aug 26, 2026
…11630)

Merged via /merge-batch (lote 2026-08-26, v3.8.51). check:file-size confirmado OK após o rebaseline.
diegosouzapw pushed a commit that referenced this pull request Aug 26, 2026
…1677)

Merged via /merge-batch (2026-08-26, v3.8.51). Boarded no worktree combinado; validação única: typecheck/complexity/cognitive-complexity/file-size/changelog verdes, lint nos mesmos 228 achados pré-existentes confirmados contra o tip puro, testes focados passando. Obrigado pela contribuição.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…amilies (diegosouzapw#10788) (diegosouzapw#11409)

Validated in a combined 3-PR batch worktree off release/v3.8.51 tip (a sibling PR from the same author, diegosouzapw#11495, was held out after an interaction-only typecheck error was isolated to it — reproduced clean without it, see diegosouzapw#11495's own comment).
- Focused tests: opencode-go-effort-aliases-6922.test.ts, opencode-go-effort-aliases-8353.test.ts, chatcore-upstream-body.test.ts — part of batch's 94/94 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Thanks for tracing this all the way to the wire format — forwarding the aliased id verbatim instead of injecting a field the non-DeepSeek families never had is exactly the right fix.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…iegosouzapw#11497) (diegosouzapw#11505)

Validated in a combined 3-PR batch worktree off release/v3.8.51 tip (a sibling PR from the same author, diegosouzapw#11495, was held out — see its own comment for the isolated finding, unrelated to this diff).
- Focused test: web-cookie-expiry.test.ts — part of batch's 94/94 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Thanks for closing a real trust gap — operators deserve to know a cookie is about to expire before a live request fails.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…s + construction to first use (diegosouzapw#11220) (diegosouzapw#11421)

Validated in a combined 3-PR batch worktree off release/v3.8.51 tip (a sibling PR from the same author, diegosouzapw#11495, was held out — a typecheck error in zai-web.ts only reproduced with this PR + diegosouzapw#11495 boarded together, and cleared without diegosouzapw#11495; isolated this PR alone confirmed clean on its own too, so the interaction belonged to diegosouzapw#11495's side — see its comment).
- Golden lock: executor-map-golden.test.ts — passes byte-identical (same keys, classes, provider identities, dispatch guards)
- Focused tests part of batch's 94/94 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Thanks for the measured, careful methodology here — the golden-lock contract plus the isolated DATA_DIR benchmarking make this an easy PR to trust despite the wide surface (72 files).
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…iegosouzapw#11488) (diegosouzapw#11495)

Merged via /merge-batch (lote 2026-08-26, v3.8.51). Boarded no worktree combinado junto com outras ~30 PRs; validação única: typecheck/complexity/cognitive-complexity/changelog-integrity verdes, file-size rebaseado onde necessário (crescimento legítimo), lint com os mesmos 228 achados pré-existentes confirmados via sonda contra o tip puro (não introduzidos por este lote), e ~370 testes focados (unit + vitest) passando. Obrigado pela contribuição.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…apw#11495/diegosouzapw#11561 growth (diegosouzapw#11630)

Merged via /merge-batch (lote 2026-08-26, v3.8.51). check:file-size confirmado OK após o rebaseline.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ep query (diegosouzapw#11677)

Merged via /merge-batch (2026-08-26, v3.8.51). Boarded no worktree combinado; validação única: typecheck/complexity/cognitive-complexity/file-size/changelog verdes, lint nos mesmos 228 achados pré-existentes confirmados contra o tip puro, testes focados passando. Obrigado pela contribuição.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(resilience): extend token health-check sweep to web-cookie connections

2 participants