Files
OmniRoute/changelog.d
Bob.Hou d6f315018a fix(chat): continue after a server-owned tool on Chat Completions (#12867)
Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437, ESLint 0 erros nos 152 arquivos alterados, e a suíte vitest:ui completa (2149) verde.

Sobre esta PR especificamente: rodei os **23 arquivos de teste** que ela toca sobre o tip final, depois do merge da base — **392/392**. A migration `174_server_tool_executions.sql` não colide (o tip está em 173, e você já a renumerou em `c35f0fd7`).

O dono foi consultado antes do merge, porque o loop está atrás da flag `SERVER_OWNED_TOOL_LOOP_ENABLED` mas o primeiro send não-streaming mudou de dono sem flag, e a verificação manual em combo com Memory continuava desmarcada. A condição dele foi: entra se os testes focados passarem aqui. Passaram.

O lock de passthrough (`fetchCalls.length === 1`) é a parte que mais me convenceu — o double-dispatch que um `if (stream)` em volta do send existente causaria é exatamente o tipo de regressão que não aparece em teste de comportamento, só em contagem de chamada.

**Três ajustes meus na sua branch:**

1. `tests/unit/chatcore-stream-error-result.test.ts` procurava `"const legResult = await runNonStreamingProviderLeg"`, mas o seu commit final `6077b9dd` passou a reatribuir `legResult` e trocou para `let`. O guard falhava na sua própria branch (confirmei que o arquivo e o `chatCore.ts` eram byte-idênticos ao head da PR, então não era efeito da leva). Passou a aceitar `const|let` — a intenção do guard é o try/catch em volta da chamada, não a palavra-chave.

2. `tests/integration/skills-pipeline.test.ts` foi de 1156 para 1338 linhas e estourou o `testCap` de 1200. Segui o mesmo caminho que você já tinha tomado em `a1d2d20d` para os testes unitários: extraí os três casos do server-owned tool loop para `tests/integration/server-owned-tool-loop-pipeline.test.ts` (259 linhas), com instância própria do harness. O glob `tests/integration/*.test.ts` pega o arquivo novo sem registro adicional. 3/3 verdes isolados.

3. O arquivo novo herdou cinco `any` do original — que só passavam por estarem congelados no `eslint-suppressions.json` sob o nome antigo. Tipei como `Record<string, unknown>`. E `tests/unit/non-streaming-finalization.test.ts` tinha dois argumentos não usados em `trackPendingRequest`, agora prefixados com `_`.

Nada disso toca produção nem enfraquece asserção.
2026-09-07 09:15:00 -03:00
..
2026-07-13 09:12:40 -03:00

changelog.d/ — changelog fragments

A PR never edits CHANGELOG.md directly during the cycle. Instead it adds ONE new file here — its changelog entry as a fragment. Two PRs never touch the same file, so changelog merge conflicts (the "CHANGELOG-eat" cascade that forced a re-sync push + full CI re-run after every sibling merge) are structurally impossible.

Convention

Directory Aggregates under
features/ ### ✨ New Features
fixes/ ### 🐛 Bug Fixes
maintenance/ ### 📝 Maintenance
  • Filename: <PR-number>-<short-slug>.md (e.g. fixes/6700-dockerfile-better-sqlite3.md). The PR number prefix keeps aggregation order deterministic.
  • Content: the exact bullet line(s) that should land in CHANGELOG.md, starting with - . Multi-line (continuation) bullets are fine. Keep the repo's credit format: (#PR — thanks @user).
  • One fragment per PR (rarely more, e.g. a PR that both fixes and adds).

Example

changelog.d/fixes/6496-cloudflare-relay-worker-syntax.md:

- **fix(providers):** Cloudflare relay Worker deploys use Service Worker syntax with `body_part` metadata ([#6496](https://github.com/diegosouzapw/OmniRoute/pull/6496)) — thanks @SeaXen

Aggregation

The release captain (or /generate-release) folds all fragments into CHANGELOG.md and deletes them:

node scripts/release/aggregate-changelog.mjs            # write + delete fragments
node scripts/release/aggregate-changelog.mjs --dry-run  # preview only

Fragment well-formedness is enforced by npm run check:changelog-integrity (the same gate that guards against CHANGELOG-eat for legacy direct edits).