Repository navigation
feat(providers): verify-only health sweep for web-cookie connections (#11488) - #11495
diegosouzapw merged 3 commits into
Conversation
…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
|
CI triage for the current red batch (Docs Gates / unit shards / Vitest / ESLint-suppressions): reproduced at today's merge ref ( This branch's own receipts stay green: |
|
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:
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. |
…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.
…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.
…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.
|
Fixed — took your option 1: the interval gate now compares against the injected if (lastCheckMs > 0 && new Date(now).getTime() - lastCheckMs < intervalMin * 60 * 1000) return true;Production semantics are unchanged — the sweep caller ( Receipts on
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.
|
Follow-up covering the repo's PR gates (copilot-instructions.md): Repro test — your reported defect was encoded as a test (the failing Coverage gate (
Touched leaf module ( 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 Branch tip: |
d351960
into
diegosouzapw:release/v3.8.51
…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.
…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.
…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.
…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).
…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.
…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.
…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.
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, …) stayedtestStatus: "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.src/lib/tokenHealthCheckWebCookie.ts(same injectable-DI pattern astokenHealthCheckKimi.ts/tokenHealthCheckCursor.ts) probes catalogued cookie providers viavalidateWebCookieProvider— the generic session-ping the dashboard Test button falls back to, proxy-fallback transport included.lastHealthCheckAtstamp (no per-tick log noise);AUTH_007 / SESSION_EXPIRED→ terminalexpiredwitherrorCode: session_expired,lastErrorSource: webcookie;unsupported→ stamp only, never a state flip.healthCheckIntervalhonored (default 60 min, 0 = off).kimi-webkeeps 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
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.tests/unit/token-health-check-kimi.test.ts4/4 pass.npx tsc --noEmit -p tsconfig.json: zero diagnostics in the three touched files.npm run check:file-size: OK.src/lib/tokenHealthCheck.ts(@/lib/localDbrestricted-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)