Commit Graph

4115 Commits

Author SHA1 Message Date
diegosouzapw
11e82e022e Merge remote-tracking branch 'origin/release/v3.8.51' into fix/release-v3.8.51-basereds-orphans 2026-09-11 14:55:08 -03:00
Dizzle
cfa2fc7548 fix(sse): rotate opencode accounts on transient 5xx (#12975)
Reconciled and merged. This branch was stacked on #12941, which has since landed, so it read as 1484 additions across 16 files and CONFLICTING. I merged the current `release/v3.8.51` into it rather than rewriting your branch: `open-sse/executors/opencode.ts` conflicted in six places where your side was a strict superset of the squashed #12941, and the tip had touched that file through nothing but #12941, so your side was taken whole. The PR now reads as its real 12 files, +839/-25.

The change itself is right: a flapping upstream 5xx aborting the whole agentic chain is exactly the case where rotating to the next healthy account is safe, and keeping it a separate arm from the 400-empty branch matters because that one has to clone-read the body while this one never touches it. Threading `correlationId` through so interleaved requests stay attributable — and never fabricating one when absent — is the right discipline.

Both geo-block regression suites pass alongside the new ones (43/43 across the five opencode test files), which is what proves the conflict resolution preserved #12941's behaviour.

I also tightened the batch's file-size rebaseline here: `src/sse/handlers/chat.ts` needed no bump at all (it lands at 2452, under its existing 2458 freeze) and `open-sse/executors/base.ts` needed only your +2. An earlier measurement had included a local prettier reformat that is not part of this branch.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the rest of this batch — zero conflicts between them.

- `typecheck:core` clean; `check:changelog-integrity` OK
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 86 focused assertions green across the batch's 10 unit test files, plus 16/16 on the v1 plugin option schema and 16/16 on the v2 option tests
- `check-file-size` rebaselined for this batch's real growth (annotation `_rebaseline_2026_09_11_mergebatch_v3851_maxmad_opencode`, landed on #13141). `open-sse/utils/stream.ts` was deliberately left frozen: it is already 3115 > 3098 on the pure tip with zero contribution from this batch.

⚠️ base-red inherited: #12732 — `Docs Gates`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and the `stream.ts` freeze above). None of them touch these diffs.

Thanks @maxmad64bis.
2026-09-11 13:57:28 -03:00
Dizzle
a19bb2227f fix(providers): lock opencode model on upstream 400 model-unavailable (#13146)
Scoping the lock to the MODEL rather than the connection is the right layer for a `400 "Model is unavailable"` — a multi-day upstream outage on one model should not darken the account. Good discipline on the two allowlist-adjacent changes: gating the `markAccountUnavailable` branch on `ruleScope === "model"` AND `status === 400` leaves every other status on its existing path, and deliberately not widening `FULL_TEXT_RULE_PROVIDERS` keeps the #10880 egress-bucketed 429 classification intact. Reading the cooldown from the rule instead of a literal at the call site is what makes it self-healing.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the rest of this batch — zero conflicts between them.

- `typecheck:core` clean; `check:changelog-integrity` OK
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 86 focused assertions green across the batch's 10 unit test files, plus 16/16 on the v1 plugin option schema and 16/16 on the v2 option tests
- `check-file-size` rebaselined for this batch's real growth (annotation `_rebaseline_2026_09_11_mergebatch_v3851_maxmad_opencode`, landed on #13141). `open-sse/utils/stream.ts` was deliberately left frozen: it is already 3115 > 3098 on the pure tip with zero contribution from this batch.

⚠️ base-red inherited: #12732 — `Docs Gates`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and the `stream.ts` freeze above). None of them touch these diffs.

Thanks @maxmad64bis.
2026-09-11 13:51:24 -03:00
Dizzle
cc4f7ed1c4 fix(logging): prefer the pipeline over the raw bodies in call-log storage and detail enrichment (#13147)
Both halves are right, and shipping them together is justified: the ladder dropping `pipeline` first threw away the upstream answer while keeping a prompt it already had a copy of, and `resolvePreviousResponseState` rebuilds continuation history out of exactly that field. The detail-panel fix has to ride along because the size-limit placeholder is a non-empty string, so fixing the ladder alone would let it overwrite a good payload. Re-checking emptiness per side after reading is the actual bug — `responseBody` being one value for both sides is what let a provider payload show as the client response.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the rest of this batch — zero conflicts between them.

- `typecheck:core` clean; `check:changelog-integrity` OK
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 86 focused assertions green across the batch's 10 unit test files, plus 16/16 on the v1 plugin option schema and 16/16 on the v2 option tests
- `check-file-size` rebaselined for this batch's real growth (annotation `_rebaseline_2026_09_11_mergebatch_v3851_maxmad_opencode`, landed on #13141). `open-sse/utils/stream.ts` was deliberately left frozen: it is already 3115 > 3098 on the pure tip with zero contribution from this batch.

⚠️ base-red inherited: #12732 — `Docs Gates`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and the `stream.ts` freeze above). None of them touch these diffs.

Thanks @maxmad64bis.
2026-09-11 13:51:21 -03:00
Dizzle
9f0d54a48f fix(combo): parse numeric-epoch rateLimitedUntil in hasFutureRateLimitUntil (#13141)
Correct and well-traced: `rate_limited_until` is TEXT but one write path stores a bare epoch that SQLite coerces to `"1781696905131.0"`, which `new Date()` alone reads as `NaN` — so a still-cooling connection looked available and combo fed it traffic that could only come back 429. Routing both readers through the existing tolerant normalizer is the minimal fix, and keeping unreadable values fail-open is the right default. The #3954/#3995 lineage explains exactly why this function never inherited the normalization.

This PR also carries the batch's file-size rebaseline, since it merges first and the ceiling has to cover every intermediate state.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the rest of this batch — zero conflicts between them.

- `typecheck:core` clean; `check:changelog-integrity` OK
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 86 focused assertions green across the batch's 10 unit test files, plus 16/16 on the v1 plugin option schema and 16/16 on the v2 option tests
- `check-file-size` rebaselined for this batch's real growth (annotation `_rebaseline_2026_09_11_mergebatch_v3851_maxmad_opencode`, landed on #13141). `open-sse/utils/stream.ts` was deliberately left frozen: it is already 3115 > 3098 on the pure tip with zero contribution from this batch.

⚠️ base-red inherited: #12732 — `Docs Gates`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and the `stream.ts` freeze above). None of them touch these diffs.

Thanks @maxmad64bis.
2026-09-11 13:51:13 -03:00
anhtahaylove
20abd89d7c fix(acp): bound session output buffers and reset stderr per prompt (#13100)
Keeping the tail is the right direction — `sendPrompt` resolves with the stdout collected since the prompt was written and stderr is read for diagnostics after a failure, so the newest output is what callers actually use. The `[...output truncated...]` marker keeps it from being silent. Resetting `stderrBuffer` alongside `stdoutBuffer` fixes the subtler half: diagnostics for one prompt were carrying stale output from every earlier one.

Verified on the tree that actually ships — your branch merged onto the current tip, which already carries #13096: `appendCapped()` and `settle()` coexist cleanly and all 7 assertions across both ACP test files pass together.

I reformatted the changelog fragment to the `changelog.d` convention (`- **fix(scope):** …`) before merging — `check:changelog-integrity` rejects a fragment that does not start with a markdown bullet, which is the same gate your #13158 was about. Wording is yours, unchanged in substance.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:32:51 -03:00
anhtahaylove
e1cfdb5e48 fix(acp): release listeners, timers and sessions on every sendPrompt outcome (#13096)
`acpManager` being a module-level singleton is what turns this from a per-call leak into unbounded growth — the `MaxListenersExceededWarning` at 11 is the visible symptom. Routing every outcome through one `settle()` is the right shape, and deleting the session on the child's own exit fixes the map growth that `getActiveSessions()`'s `alive` filter was hiding.


I reformatted the changelog fragment to the `changelog.d` convention (`- **fix(scope):** …`) before merging — `check:changelog-integrity` rejects a fragment that does not start with a markdown bullet, which is the same gate your #13158 was about. Wording is yours, unchanged in substance.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:30:44 -03:00
anhtahaylove
d61b1727cd fix(cli-helper): clear the log stream timeout on the abort path (#13114)
`stop()` is the normal lifecycle for a `follow: true` stream, not an edge case, so the `signal.aborted` early return skipping `clearTimeout` leaked one armed timer per stop. Cancelling the reader on the early loop exit closes the second half.


I reformatted the changelog fragment to the `changelog.d` convention (`- **fix(scope):** …`) before merging — `check:changelog-integrity` rejects a fragment that does not start with a markdown bullet, which is the same gate your #13158 was about. Wording is yours, unchanged in substance.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:30:38 -03:00
anhtahaylove
99fb441434 fix(db): release process listeners when a node:sqlite adapter closes (#13109)
Right precedent — #7494 fixed exactly this for the sql.js adapter and the `node:sqlite` one never got the same treatment, even though it is the default driver whenever better-sqlite3 is unavailable. `/api/db-backups/import` opening a throwaway adapter per request makes it reachable.


I reformatted the changelog fragment to the `changelog.d` convention (`- **fix(scope):** …`) before merging — `check:changelog-integrity` rejects a fragment that does not start with a markdown bullet, which is the same gate your #13158 was about. Wording is yours, unchanged in substance.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:30:32 -03:00
anhtahaylove
67618978b0 fix(gamification): close the badge SSE stream when the signal is already aborted (#13106)
Correct: an `abort` listener registered on an already-aborted signal never fires, and `safeEnqueue` can't save it because enqueuing into an unread stream only buffers. The abort-later test earning its keep as a guard on the healthy path is the right instinct.


I reformatted the changelog fragment to the `changelog.d` convention (`- **fix(scope):** …`) before merging — `check:changelog-integrity` rejects a fragment that does not start with a markdown bullet, which is the same gate your #13158 was about. Wording is yours, unchanged in substance.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:30:27 -03:00
anhtahaylove
85a5126dba fix(compression): terminate idle workers on eviction (#13091)
`remove(slot, false)` dropped the slot without `terminate()`, leaving the OS thread and its heap alive — invisible to RSS, which is why 55 orphaned `MessagePort`s took 16 h to surface. Removing the parameter rather than keeping it is the correct call: the pool was its only owner.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:27:45 -03:00
anhtahaylove
b1733d3c83 fix(plugins): make SIGKILL escalation idempotent per child (#13092)
`once` only detaches when exit actually fires, so a plugin trapping SIGTERM accumulated one listener and one timer per hook timeout. Keying idempotence on the child via a `WeakSet` is right — a second SIGKILL timer would only re-signal a corpse.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:27:42 -03:00
anhtahaylove
66330cc724 fix(compression): pass a URL object when spawning the LLMLingua worker (#13093)
`node:worker_threads` only treats a `URL` instance as a `file:` URL — a string must be a relative path. The silent `catch {}` in `pump()` made this degrade compression to a passthrough while still reporting success, which is the worst shape for it.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:27:38 -03:00
anhtahaylove
a3fa6cf524 fix(traffic-inspector): release WS subscriber and ping timer on a dead socket (#13155)
Attaching `close`/`error` before subscribing closes the window where a resource is held with no live cleanup path, and the destroyed-socket re-check after the handshake covers the in-flight case. `write()` not throwing synchronously is exactly why the old `try/catch` never fired.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:27:34 -03:00
anhtahaylove
30c96d43a5 fix(telegram): bound the per-user API key cache (#13166)
`resolveUserApiKey()` keyed an uncapped Map on an id taken straight from the webhook body. The LRU's recency test is what keeps this from regressing into a clear-when-full cache.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:27:30 -03:00
anhtahaylove
658153c7b0 fix(stream): release the upstream body when the JSON-to-SSE sniff times out (#13171)
The sniff loop abandoned the upstream reader when `withBodyTimeout()` rejected. The `handedOff` flag correctly spares the two success paths from cancellation; the second test guards that direction.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:27:26 -03:00
anhtahaylove
a1b9b02d5d fix(telegram): authenticate webhook deliveries with the Telegram secret token (#13175)
Real abuse vector: the bot-webhook branch reached `proxyChat()` — which mints an API key and spends upstream quota — with nothing proving the caller was Telegram. Fail-closed 503 when the secret is unset is the right default.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:27:23 -03:00
anhtahaylove
edfcb8be17 fix(test): resolve the WebDAV handler path with fileURLToPath (#13196)
Correct: `URL.pathname` is a URL path, so on Windows it yields `/C:/...` and `path.resolve` produces the doubled `C:\C:\` prefix. `fileURLToPath` is the right decoder and also un-escapes `%20`. All 37 WebDAV tests green here.

---

Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.

- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches

⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.

Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
2026-09-11 13:27:14 -03:00
diegosouzapw
ea7ba79a73 fix(sse): restore credential refresh, failure persistence and codex image errors
Three more regressions from the #12867 pipeline extraction, all red on the
base tip:

- the non-streaming leg stopped refreshing credentials after a 401 and
  stopped persisting failure state, so a Copilot token was never retried
  and per-model quota locks lost their helper references;
- handleImageGeneration (codex) crashed reading fields off a body that
  #12506 now sanitizes before it reaches the classifier;
- the pipeline discarded the raw upstream body, which provider-error
  classification needs (quota vs rate-limit vs ban) — surfaced as
  rawMessage/upstreamBody, both internal to the classifier.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-09-11 12:56:54 -03:00
diegosouzapw
3603a7b246 test(sse): pin the pipeline error-classification and Kiro stream contracts
Regression guards for the two defects above: the pipeline must surface the
upstream error code/type, and a Kiro error frame now terminates the stream
after forwarding response.failed (the #12506 contract the sibling
stream-passthrough suite already encodes).

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-09-11 12:56:15 -03:00
diegosouzapw
47b6fb837a Merge branch 'fix/v3851-basereds-orphans-g4' into fix/release-v3.8.51-basereds-orphans 2026-09-11 12:55:06 -03:00
diegosouzapw
e25b706c56 test(lease): re-inventory hard-lease call sites after the pipeline extraction
Four drifts, all from PRs merged into the tip on 2026-09-07:

- #12867 extracted chatCore.ts's streaming execution loop into
  chatCore/providerExecutionPipeline.ts. Its two getProviderCredentials() sites
  now go through the injected `connection.getProviderCredentials` handle, which
  the bare-identifier AST scan never saw — the sites would have left the
  inventory unnoticed. Count property-access calls too and inventory the new file.
- #12867 also re-expressed the codex 429 managed-lease fence: the inline
  `provider === "codex" && !managedLease` became `allowAccountRotation:
  !managedLease && …` in chatCore.ts, gated in the pipeline as `canRotateAccount`.
  Assert both halves of that seam instead of the vanished inline form.
- #12746 moved combo.ts's getProviderConnectionById into
  combo/executeTargetGates.ts (class B, unchanged).
- #12805 added the Grok Build reset-credit path: src/lib/usage/grokResetCredits.ts
  (class B — same isConnectionUnavailableToAuxiliaryActivity fence as its codex
  sibling, so it also joins auxiliaryIsolationSources) and
  src/app/api/usage/codex-reset-credit/route.ts (class C — resolves the
  connection's provider to pick a library, never to serve a request).

Also pin CATALOG_BUILD_TIMEOUT_MS in models-catalog-route.test.ts: #12627's 8s
cold-build bound is sized for a warm production process, and a tsx test runner
building the full catalog from a fresh SQLite file crosses it (10-13s observed),
returning a `catalog_build_timeout` error body with no `data` array. The bound's
own behavior stays covered by 12627-catalog-inflight-timeout.test.ts.
2026-09-10 19:13:05 -03:00
diegosouzapw
7edda9c30a test(combo): follow the provider-cooldown source guard into the split modules
#12746 (executeTarget → gates/attempt/loop) and #12811 (round-robin) moved the
combo dispatcher out of open-sse/services/combo.ts. The #5976 invariant moved
with it intact — executeTargetAttempt.ts:1169 and roundRobinCombo.ts:1049 both
still exclude a 500/429 on a per-model-quota provider before recording a provider
cooldown — but the guard only read combo.ts and went red on an empty file.

Scan the whole combo surface instead of one filename and require the skip in
EVERY module that calls recordProviderCooldown(), with the pattern matched on
whitespace-normalized source so a prettier rewrap cannot silently disarm it. The
invariant asserted is unchanged; the guard is now stronger than the single
substring check it replaces.
2026-09-10 19:12:46 -03:00
diegosouzapw
b4616e4316 fix(catalog): keep operator custom models out of the live-sync reader
#12934 unioned customModels into getAllActiveSyncedModels() alongside the
dispatch-time readers it was actually fixing (#12597: getActiveSyncedCatalog,
reconcileProvidersWithActiveSyncedCatalog, getActiveProvidersWithSyncedModel).

getAllActiveSyncedModels() is not a dispatch reader. Its three consumers read it
as "what the provider's live sync reported": /v1/models feeds its synced-emission
loop (and has a separate custom-model pass right after, which owns the
specialty-registry dedupe, hidePaid and the vision overrides), /api/models uses it
to decide whether an exclusive-listing provider suppresses a static row, and
getSyncedAutoAliases derives tier aliases from it. Blurring custom rows into
"synced" made a custom embedding/rerank model on jina-ai come out as the alias
row `jina/<id>` (parentless primary) with the registry's canonical
`jina-ai/<id>` demoted to its child — the inverse of the identity every other
specialty model of that provider carries, and it also dropped the registry's
`dimensions`.

Drop the union there only; the dispatch trio keeps it, so #12597's tests and the
400-on-picker-model fix are untouched. Locked by a new case in
custom-models-live-catalog-12597.test.ts and by both jina cases in
models-catalog-route.test.ts ("does not duplicate imported/custom Jina specialty
models"), which encode the two identities side by side.
2026-09-10 19:12:06 -03:00
diegosouzapw
b3d3d9524c fix(combo): survive a malformed customModels row when building auto/* pools
prepareVirtualAutoComboInputs() reads getCustomModels(providerId) straight into
`for (const m of customModels) if (m.id ...)`. That blob is operator-writable and
is returned as raw parsed JSON, so a null / non-object row threw "Cannot read
properties of null (reading 'id')" and EVERY auto/* combo failed to materialize
("[catalog] Could not materialize built-in auto model auto/<id>"), silently
degrading the whole zero-setup routing surface to its minimal catalog entries.

Filter the rows to objects first, the same way the /v1/models custom-model pass
already does. Regression test in combo-auto-pool-visible-only.test.ts fails with
the original TypeError before the guard.
2026-09-10 19:11:36 -03:00
Nguyen Thanh Dat
af49d4972e fix(stream): accept the buffer size glm.ts has been passing since #12179 (#12925)
Rebased onto the tip and completed, per the maintainer's call to finish the wiring rather than merge the capability alone.

What changed since your version:

The tip had already cleared the TS2554 by deleting the 16th argument, leaving a comment that the highWaterMark stays at the helper default. So the base-red you found is gone, but the 64 KB #12179 asked for was still not applied and your new parameter had no caller. glm.ts now passes it, which is what turns the capability into the fix.

Your test file also hung the runner: every stream createSSEStream builds arms a 10s idle watchdog via setInterval in start, and nothing cancelled them, so node:test waited on a non-empty event loop long after the assertions passed. Cancelling each readable in an after hook runs the cancel handler that clears the timer — the file now reports in about 7 seconds. Worth knowing for future stream tests.

Your five assertions are unchanged and all pass. Reading the writable's desiredSize to measure the queue budget the stream was actually built with, rather than standing in for it, is the detail that makes this testable at all — and the 0-budget case pinning `??` against `||` is the kind of thing that silently rots otherwise.

Thank you also for separating your own red checks from the base's and reporting what you found there. That is how #12919's identical failures got explained instead of chased.
2026-09-10 18:25:31 -03:00
Nguyen Thanh Dat
22473dee50 feat(providers): add EURouter as an OpenAI-compatible gateway (#12985) (#13025)
Rebased onto the release tip after #13024 landed: both PRs extend the same three registration files, so the sibling merge turned this into a conflict. The resolution is additive — both catalog entries kept, both registry imports kept, both base URLs kept — and EURouter stays in AGGREGATOR_PROVIDER_IDS while GreenPT stays out, exactly as each PR argued. 14 provider tests pass on the rebased branch and the file-size gate is green under the annotated rebaseline.

Thank you for re-checking the endpoint live instead of trusting the report, and for the sovereignty caveat. Naming the upstreams from EURouter's own catalog — Claude Sonnet served by AWS Bedrock, 19 models owned by openai — and then writing an apiHint that says routing rather than residency is the kind of care that keeps a provider entry honest. The test asserting the copy contains none of "residency", "stays in the EU", "EU-hosted" or "sovereign" is a good guard against that drifting later.
2026-09-10 18:16:32 -03:00
Nguyen Thanh Dat
2b9e7fb3ec feat(providers): add GreenPT as an OpenAI-compatible provider (#13024)
Merged with a rebaseline commit added on top of your branch: check:file-size freezes the gateways catalog at 1462 lines, so any new entry fails the gate on arrival. The annotation covers this entry and EURouter's (#13025) together, following the route every previous gateway entry took (#11786 seekai, #10987 logfare, #10668 tabitoken, #10531 freebuff, #11631 1min.ai) — the file is declarative data already split into six family files, so splitting it for two entries would break the semantic-families rule.

Validated in a combined worktree with 13 sibling PRs: 132 focused tests pass, typecheck:core clean, file-size green after the rebaseline.

Thank you for stating plainly what you did not verify. "The endpoint exists and is key-gated; catalog, streaming and tool calls not exercised" is worth more than a confident entry that turns out to be guesswork, and the conservative entry that follows from it — empty models, no capability declared, hasFree false with the billing shape spelled out — is exactly right.
2026-09-10 18:14:04 -03:00
Nguyen Thanh Dat
9a56147019 fix(skills): read positionals declared with .addArgument() (#13009)
Approved by the maintainer for the agent-instruction surface it touches: the SKILL.md change is regenerated output from the corrected parser (`resilience set` -> `resilience set <name>`), restoring the required argument the published page had been hiding. No hand-written directive was added.

Boarded with 13 sibling PRs and validated as a set: 132 focused tests pass, typecheck:core clean, changelog integrity and file-size gates green.

Thank you — the table contrasting the declared argument against the published page is what made the second case (an agent told to run `resilience set` with no argument) visible as more than cosmetic.
2026-09-10 18:13:37 -03:00
Nguyen Thanh Dat
751247a143 fix(security): scan both ends of an oversized body, not just the front (#13104)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:13:20 -03:00
Nguyen Thanh Dat
567abb5d68 fix(security): scan the text a tool_result carries (#13101)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:13:16 -03:00
Nguyen Thanh Dat
403a1a697d fix(guardrails): mask PII inside a tool_result's nested content (#12930)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:13:12 -03:00
Nguyen Thanh Dat
f2d5728cfd fix(dashboard): test Responses nodes on /v1/responses, not chat completions (#13070) (#13087)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:13:08 -03:00
Nguyen Thanh Dat
0a314c84de fix(translator): treat contentSchema and unevaluatedItems as schema slots (#13110)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:13:05 -03:00
Nguyen Thanh Dat
1929aa656a fix(validation): accept a null dailyQuotaResetTimezone (#13066) (#13083)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:13:01 -03:00
Nguyen Thanh Dat
4edc3d57d0 fix(azure): match the generation, not one release, for max_completion_tokens (#13007)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:12:57 -03:00
Nguyen Thanh Dat
a6f28210de fix(logs): match the in-memory call-log filter to the SQL one it re-applies (#12896)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:12:54 -03:00
Nguyen Thanh Dat
5df94f8b05 fix(bedrock): resolve context limits for every vendor prefix, not just anthropic (#12921)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:12:49 -03:00
Nguyen Thanh Dat
e1a1290fde fix(compression): keep tool_result blocks first when aging annotates a turn (#12920)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:12:45 -03:00
Nguyen Thanh Dat
ee21e7d2c9 fix(a2a): build the status agent card from the request that asked for it (#12918)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
2026-09-10 18:12:41 -03:00
Diego Rodrigues de Sa e Souza
0549dcfc36 fix(api): scope batch bulk-delete to the calling API key (#13211)
GHSA-wvxc-jp3v-5mg5: `DELETE /api/v1/batches/delete-completed` deleted the
completed batches of EVERY api key on the instance and nulled the contents of
every file those batches referenced. Any ordinary inference key reached it —
including one with `scopes: []` — and no victim key, batch id or file id was
needed.

Two defects stacked in one endpoint:

  - `deleteCompletedBatches()` carried no `api_key_id` predicate. The file
    SELECT, the checkpoint DELETE and the batch DELETE were all instance-wide.
  - The route only checked that SOME key was present (`!scope.apiKeyId` → 401),
    never that the caller owned anything, and called the helper bare.

The helper now takes `apiKeyId` and scopes all three statements to it; the route
passes the caller's key and omits it only for session auth, so the operator's own
dashboard keeps its instance-wide cleanup and an API key clears only its own
completed batches.

None of this is a new pattern. `listBatches(apiKeyId?)` and
`countBatches(apiKeyId?)` in the same module already scope by `api_key_id`, and
`batches/[id]/route.ts` already gates per-record access with `scopeCheck` —
session auth sees everything, a key sees only its own. This one helper was the
one that never got it, which is why the fix reuses the shape instead of
inventing a second convention.

tests/unit/batch-delete-completed-ownership-wvxc.test.ts — 5 tests, 4 red before
the fix, including the two that prove the cross-tenant destruction (another
key's batch survives; another key's file content survives). It also pins the
instance-wide dashboard sweep so the fix cannot be "tightened" into breaking the
operator's own cleanup, and a source guard that the route never calls the helper
bare again.

Reported privately via GHSA-wvxc-jp3v-5mg5.

Closes GHSA-wvxc-jp3v-5mg5
2026-09-10 13:27:10 -03:00
Diego Rodrigues de Sa e Souza
393cfdd660 fix(test): make the ToS heading guard actually require the parentheses (#13228)
CodeQL js/useless-regexp-character-escape (#994-#997) on one line, and it is a
real defect rather than the usual query noise.

The assertion built its pattern in a TEMPLATE literal:

    new RegExp(`\(\s*${String(tos?.actual)}\s*\)`)

JavaScript resolves the escapes before RegExp ever sees the string: `\(` becomes
"(" and `\s` becomes the LETTER "s". The compiled pattern was `(s*16s*)` — a
capture group around optional "s" characters — so it matched any heading merely
CONTAINING the number. The literal parentheses this guard exists to require were
never checked, and it passed on exactly the headings it was written to reject:

    /(s*16s*)/.test("### Caution — clauses worth checking 16")   // true

Doubled the backslashes so they survive the template literal, and routed the
interpolated value through an `escapeRegExp` helper — the count is a number
today, but interpolating an unescaped value into a regex source is the same
class of bug one refactor away.

Added a second test that pins the behaviour rather than the spelling: the
pattern must REJECT a heading carrying the count without parentheses, and accept
it with them (including inner whitespace). Before this fix that test fails.

4/4 green against the real docs/reference/FREE_TIERS.md heading.
2026-09-10 13:27:02 -03:00
Dizzle
81bf3cc36e docs(checks): keep doc counts honest — headings, rankings, catalog, weights, quality gate and scoring diagram now covered (#12507)
Estender o gate de contagens para headings, rankings, catálogo, pesos, quality gate e o diagrama de scoring é exatamente o tipo de trabalho que evita a classe inteira em vez de um caso.

Falo por experiência desta campanha: o `check:docs-counts` caiu **duas vezes** hoje pela mesma causa — contagem de migration escrita à mão em três arquivos mais 41 mirrors, desatualizando a cada migration nova (#12970 e #13209). Cada superfície que este PR passa a cobrir é uma que deixa de virar base-red na mão de quem vier depois.

Revalidei sobre o tip: **19/19**, `check:docs-counts-sync` com 0 drifts, `check:docs-all` PASS, `check:doc-links` PASS.

**Integração:** dois conflitos.

1. `scripts/check/check-docs-counts-sync.mjs` — o bloco de leitura de fatos conflitou com os imports de free-tier que entraram pelo #12786/#12744 nesta campanha. Aditivo, os dois conjuntos ficaram.
2. `docs/diagrams/auto-combo-scoring.mmd` — o seu rótulo dizia `reliability (0.0000)`, mas o #12731 mergeou horas antes e passou a dar peso de reliability a todo mode pack. Ficou o rótulo do tip, `reliability (0.0000 DEFAULT, 0.03 packs, 0.04 reliable)`, que é o número real agora.
2026-09-10 10:54:38 -03:00
Markus Hartung
955b28ef5c feat(dashboard): badge a conversation that never reached a clean stop (#12717)
Uma conversa que nunca chegou a parada limpa e não sinaliza nada é o pior estado possível de UI: indistinguível de uma que terminou. O incidente que você cita no comentário do teste — stream pesado em reasoning estourando o cap do coletor no meio, deixando a conversa presa sem sinal — é exatamente o caso que justifica o badge.

Separar `resolveTurnCompletionState` de `resolveConversationStalledState` também está certo: `tool_call_pending` é um estado legítimo em voo, não uma conversa travada.

Revalidei sobre o tip: **29/29**, typecheck:core limpo.

**Nota de integração.** O `tests/unit/responses-continuation-store.test.ts` conflitou com o #12854, que anexa a própria bateria ao mesmo arquivo. Reconstruí o arquivo como append limpo — versão do tip mais o seu bloco de 184 linhas, verificado por `esbuild` antes de rodar. Registro por que importa: na primeira tentativa eu apenas retirei os marcadores de conflito, e isso enfiou os seus testes **dentro** de um objeto literal não terminado do #12854. Compilava como erro de transform, não como conflito — só apareceu ao rodar. Resolver JSON e teste "aditivamente" sem verificar a sintaxe depois é armadilha; ficou a lição.
2026-09-10 10:52:03 -03:00
Dizzle
a152eb92db fix(resilience): stop unbounded queue that hangs 6min until Aborted (#12715)
Fila sem teto que segura a request seis minutos até o cliente abortar é pior que 503 imediato: consome slot, mascara a saturação e ainda entrega erro no fim. Um orçamento `maxWaitMs` por conexão compartilhado entre gate, slot padrão do provider e fila do Bottleneck é a forma certa — o teto tem que ser um só, senão cada camada espera o seu.

O `max(perConn, upstream)` no `executionMaxWaitMs` é o detalhe que evita a correção matar request em voo, que seria trocar um defeito por outro.

Registro a atribuição: você manteve o #12635 aberto para o @Tushar49 e creditou a percepção dele (providers lentos precisam de 2min→10min por conexão) enquanto adiciona o encanamento que faltava. É o jeito certo de construir sobre PR de outra pessoa sem tomar o crédito.

Sobre o `npm run lint` desmarcado com a nota do eslint quebrado no ambiente: deixar em branco e explicar vale mais que marcar sem ter rodado. Rodei aqui: limpo.

Revalidei sobre o tip: **13/13**, typecheck:core limpo, check-file-size OK. O `file-size-baseline.json` conflitou com os rebaselines desta campanha — resolvido aditivamente, JSON revalidado com `json.load`.
2026-09-10 10:49:34 -03:00
Ravi Tharuma
d6a61074dc fix(quota): align AUTH window labels with usage API and clarify 503 (#12884)
Um `ALL_TARGETS_SKIPPED` 503 que não diz qual janela esgotou é opaco justamente no momento em que o operador mais precisa saber. Alinhar os rótulos de janela AUTH com os da API de uso fecha a outra metade: dois nomes para a mesma coisa fazem o dashboard e o erro parecerem discordar.

Revalidei sobre o tip: **6/6**, typecheck:core limpo, check-file-size OK.

**Dois consertos meus na sua branch.**

1. `typecheck:core` falhava com `TS2345` em `comboAttemptLoop.ts` (linhas 130 e 416): o `QuotaSkipTarget` declarava `connectionId?: string`, mas o `ResolvedComboTarget` carrega `string | null` para alvo não-pinado. Alarguei para `string | null` no tipo de diagnóstico em vez de estreitar o call site — o módulo só **lê** o campo e a linha 29 já narrowa com `typeof === "string"`, então null não custa nada ali. Isso apareceu porque o `comboAttemptLoop` mudou de forma no #12746/#12811, mergeados nesta mesma campanha depois que você cortou a branch.

2. O `roundRobinCombo.ts` foi de 1198 para 1205 e cruzou o teto de 1200 para arquivo novo. Congelei com justificativa: o arquivo já nasceu em 1198 quando o #12811 o levantou de dentro do `combo.ts`, e os diagnósticos em si vivem no `quotaSkipDiagnostics.ts`, sob o cap. Registrei que a próxima extração natural é o corpo do attempt loop, mas que ele acabou de ser movido e deve assentar antes de ser cortado de novo.
2026-09-10 10:47:11 -03:00
Dizzle
4c10baa644 fix(dashboard): explain silent Radar cells on hover and gate them (#12937)
Validado numa worktree combinada com a onda de dashboard/monitoring desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-api-typecheck OK (289), check-file-size OK após rebaseline, 130/131 nos testes focados — a falha restante é asserção de tempo de parede sob carga, verde 6/6 isolada.

Sentinela que não se explica (`—`, `?`) faz o leitor inventar a razão. Explicar no hover é metade; o `check:radar-sentinels` é a outra — sem o gate, a explicação apodrece na primeira coluna nova.
2026-09-10 10:42:48 -03:00
Markus Hartung
b516e95262 fix(conversations): show a pending spinner for unresolved tool nodes instead of "(empty)" (#12727)
Validado numa worktree combinada com a onda de dashboard/monitoring desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-api-typecheck OK (289), check-file-size OK após rebaseline, 130/131 nos testes focados — a falha restante é asserção de tempo de parede sob carga, verde 6/6 isolada.

"(empty)" para um nó de ferramenta ainda não resolvido é informação errada, não ausência de informação — o usuário lê como "não retornou nada". Spinner de pendente diz a verdade.
2026-09-10 10:42:38 -03:00
Ravi Tharuma
3e2a6d8f35 fix(credentialHealth): do not poison multi-upstream openai-compat conn on one model 402 (#12875)
Validado numa worktree combinada com a onda de dashboard/monitoring desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-api-typecheck OK (289), check-file-size OK após rebaseline, 130/131 nos testes focados — a falha restante é asserção de tempo de parede sob carga, verde 6/6 isolada.

Envenenar a conexão inteira por um 402 de **um** modelo é o erro clássico de granularidade em provider openai-compatible com múltiplos upstreams — derruba modelos que estavam saudáveis. Restringir ao modelo afetado é o comportamento correto, e é a mesma distinção que o guia de resiliência faz entre cooldown de conexão e lockout de modelo.
2026-09-10 10:42:34 -03:00
Ravi Tharuma
6029515402 fix(api): page and stream-complete GET /v1/models for large catalogs (#12882)
Validado numa worktree combinada com a onda de dashboard/monitoring desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-api-typecheck OK (289), check-file-size OK após rebaseline, 130/131 nos testes focados — a falha restante é asserção de tempo de parede sob carga, verde 6/6 isolada.

Paginar e completar o stream em `GET /v1/models` é a correção certa para catálogo grande: um payload único que cresce com o número de providers vira timeout silencioso no cliente, não erro.
2026-09-10 10:42:30 -03:00