refactor(sse): split handleChatCore into four response-path leaves (#14725)

Validated in a local merge-train on a clean-install host (FAST gates green: static + changed tests + vitest on the combined tree; audit.test.ts verified passing on base and PR tree when the machine is idle).
⚠️ base-red inherited: #15306
This commit is contained in:
Bob.Hou 2026-10-06 12:50:32 -04:00 • committed by GitHub
parent c079319e6d
commit da4ddc2d67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 5117 additions and 3006 deletions

View file

@ -523,7 +523,9 @@
"open-sse/executors/codex.ts": 1584,
"open-sse/executors/cursor.ts": 1868,
"open-sse/executors/muse-spark-web.ts": 1405,
"open-sse/handlers/chatCore.ts": 6444,
"open-sse/handlers/chatCore.ts": 6426,
"open-sse/handlers/chatCore/nonStreamingResponse.ts": 1212,
"open-sse/handlers/chatCore/streamingResponse.ts": 1308,
"open-sse/handlers/imageGeneration.ts": 3334,
"open-sse/handlers/search.ts": 1789,
"open-sse/mcp-server/schemas/tools.ts": 1621,
@ -776,6 +778,7 @@
"_rebaseline_2026_09_19_14162_native_codex_auto_resume": "PR #14162 (re-land of #13180, @mdigitalbh81 via @diegosouzapw): native Codex turn auto-resume. open-sse/services/combo/executeTargetAttempt.ts 1258->1273 (+15). Growth is 100% the PR's own, measured against the clean tip (1258 there, gate green): the pin step now advances the logical turn generation and logs the resumed provider/model when the attempt is an auto-resume dispatch, and the generation is passed into pinNativeCodexTurn — the branch has to sit at the pin site because that is the only place the winning target and effective connection are known. Covered by tests/unit/native-codex-auto-resume.test.ts + native-codex-auto-resume-guards.test.ts (15/15) and #13564's native-codex-turn-pin-model-scoped-fallback.test.ts (7/7).",
"_rebaseline_2026_09_22_14405_freetier_observed_tools_retry": "Issue #14405 own growth: open-sse/executors/opencode.ts 1251->1303 (measured after merging release/v3.8.51, whose own drift entry _rebaseline_2026_09_22_opencode_train10c_drift had already moved the ceiling 1247->1251; the extra 5 lines over the original 1247->1294 measurement are the #14148 reconciliation: freeTierRetryCtx now reads the contract attempt back from the request body via attemptFor() instead of the removed shared _contractAttempt field) (+47 irreducible call-site wiring: freeTierRetryCtx builder + direct fast-path retry call + rotation-loop refusal arm delegating to handleLoopFreeTierRefusal + api-typecheck fixes (unknown-cast on stall-guard dispatch, callback-shaped loop handler keeping private finalize methods); the retry logic itself lives in the new module open-sse/executors/opencodeFreeTierRetry.ts (131 lines, under cap) and the merge helper in opencodeFreeTierContract.ts, so no logic was added to the frozen file beyond wiring two existing dispatch arms. Compaction attempts measured and reverted: loop-call wrapper (+22 net), fast-path/loop fusion (fragile: retry-403 vs original-403 share status). Covered by tests/unit/opencode-free-tier-refusal-rotation.test.ts (3 retry tests: union success, original refusal + store untouched, no retry without names) + tests/unit/opencode-free-tier-request-contract.test.ts (2 merge tests).",
"_rebaseline_2026_09_24_14659_pool_reselect_per_attempt_growth": "PR #14659 (opt-in OPENCODE_POOL_RESELECT, default off): own growth at existing chokepoints, additive and flag-off inert, re-measured on the release tip post-#14620 merge. open-sse/executors/opencode.ts 1301->1355 (+54: flag/resolver hoist, reselect cell, 429-arm hunk after stop/park exits); open-sse/utils/proxyFetch.ts 1294->1311 (+17: reselectPoolMember field on AppliedProxySink + read-only currentAppliedProxySink getter on the shared store); src/sse/handlers/chat.ts 2563->2564 (+1: reselectPoolMember field on the sink literal); src/sse/handlers/chatHelpers.ts 1258->1303 (+45: publishPoolReselectResolver helper + one call at the capture site, resolver re-invokes pool selection). Covered by tests/unit/opencode-429-pool-reselect.test.ts (6/6 GREEN) + flag guards 58/58.",
"_rebaseline_2026_09_23_13065_chatcore_reextract": "Re-extract of #13065 onto the release/v3.8.51 tip (Diego's ask: each leaf must be an empty-diff lift of its chatCore.ts region, imports/exports aside). The two new leaves inherit the exact size of the blocks they carry out of the barrel: nonStreamingResponse.ts holds the if(!stream) leg plus the runNonStreamingProviderLeg/finalizeToolLoopError wrappers; streamingResponse.ts holds the if(stream) provider pipeline. Shrinking below the cap would mean re-modularizing beyond the approved extraction boundaries, which defeats the empty-diff review criterion. Frozen at current size: any further growth of these two files fails the gate.",
"_rebaseline_2026_09_30_15186_stream_output_model_lockout": "Model-only lockout skipped for an isolated 5xx after the stream already relayed output (locked again once 3 such failures arrive in a row with no completed stream in between): open-sse/utils/stream.ts 3297->3298 (return registerStreamTiming(sseStream, timing)), open-sse/handlers/chatCore.ts 6441->6444 (hasEmittedOutput passed to createStreamFailureFinalizers + streak reset on a completed stream + its import) and src/sse/handlers/chatHelpers.ts 1285->1286 (streamOutputEmitted read from the failure payload) -- irreducible call-site wiring. The logic lives in new modules: open-sse/utils/streamTiming.ts (output registry), open-sse/services/accountFallback/postOutputFailureStreak.ts (streak) and src/sse/services/syntheticEmptyStream.ts (isRequestScopedServerFailure); src/sse/services/auth.ts stays under its ceiling because buildExhaustionOptions now reuses the markAccountUnavailable option type. Covered by tests/unit/stream-failure-after-output-no-model-lockout.test.ts.",
"_rebaseline_2026_10_03_uniform_provider_regime_mount": "Own growth (uniform provider regime mount): src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx 1479->1483 (+4 = the PoolUpstreamRegimeLine import and its three-line conditional mount under the pool members label, next to PoolEgressObservation/PoolMemberEgressLines: rendered only when poolScope is 'provider' with a non-blank poolScopeId, which becomes the line's provider; nothing renders for other scopes or a blank id, so no conclusion without a provider). The regime itself lives outside the frozen file, all under cap: PoolUpstreamRegimeLine.tsx, the dedicated GET /api/settings/proxies/pool/uniform-egress route, readUniformEgressRegime/isUpstreamRegime in src/lib/proxyPoolEgressObservation.ts and getProviderUpstreamSummary in src/lib/db/proxies.ts. Only the mount point is irreducible. Covered by tests/unit/ui/proxy-registry-manager-regime-mount.test.tsx and tests/unit/ui/pool-upstream-regime-line.test.tsx.",
"_rebaseline_2026_10_02_admission_rejection_call_log": "PR own growth (admission rejection call-log) after extraction of the rejection-logging logic into the new src/sse/handlers/admissionRejectionLog.ts (under cap): src/sse/handlers/chat.ts 2586->2597 (+11 = one import + 10 call-site lines at the single acquire() rejection site, irreducible — plancher +6 prouve, gate exige +0). Covered by tests/unit/admission-rejection-call-log.test.ts.",

View file

@ -480,7 +480,7 @@
},
{
"name": "test",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"disposition": "separately-invoked"
},
{
@ -535,7 +535,7 @@
},
{
"name": "test:coverage:runner",
"command": "node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true NODE_OPTIONS=--max-old-space-size=8192 c8 --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 60 --lines 60 --functions 60 --branches 60 node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"command": "node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true NODE_OPTIONS=--max-old-space-size=8192 c8 --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 60 --lines 60 --functions 60 --branches 60 node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"disposition": "separately-invoked"
},
{
@ -615,22 +615,22 @@
},
{
"name": "test:unit",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"disposition": "separately-invoked"
},
{
"name": "test:unit:ci",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"disposition": "separately-invoked"
},
{
"name": "test:unit:ci:shard",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=$TEST_SHARD tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=$TEST_SHARD \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=$TEST_SHARD \"tests/unit/serial/**/*.test.ts\"",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=$TEST_SHARD tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=$TEST_SHARD \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=$TEST_SHARD \"tests/unit/serial/**/*.test.ts\"",
"disposition": "separately-invoked"
},
{
"name": "test:unit:fast",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"disposition": "separately-invoked"
},
{
@ -645,12 +645,12 @@
},
{
"name": "test:unit:shard:1",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=1/2 \"tests/unit/serial/**/*.test.ts\"",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=1/2 \"tests/unit/serial/**/*.test.ts\"",
"disposition": "separately-invoked"
},
{
"name": "test:unit:shard:2",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=2/2 \"tests/unit/serial/**/*.test.ts\"",
"command": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=2/2 \"tests/unit/serial/**/*.test.ts\"",
"disposition": "separately-invoked"
},
{

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,688 @@
/**
* One provider send. Lifted out of handleChatCore. The body is the tip
* implementation; the only change is that closed-over request state arrives
* through deps.
*/
import type { BaseExecutor } from "../../executors/base.ts";
import type { AgentGoalPolicy } from "../../utils/agentGoalPolicy.ts";
import { prepareUpstreamBody } from "./upstreamBody.ts";
import { getExecutionConnectionId } from "./executionCredentials.ts";
import {
resolveAccountSemaphoreKey,
resolveAccountSemaphoreMaxConcurrency,
} from "./executorHelpers.ts";
import {
materializeDeduplicatedExecutionResult,
stripNextMiddlewareControlHeaders,
stripStaleForwardingHeaders,
} from "./responseHeaders.ts";
import { readNonStreamingResponseBody } from "./nonStreamingResponseBody.ts";
import { wrapReadableStreamWithFinalize } from "./streamFinalize.ts";
import {
normalizeExecutorResult,
executeWithUpstreamStartTimeout,
resolveConnectionTimeoutMs,
} from "./upstreamTimeouts.ts";
import { getCodexClientSessionId } from "../../config/codexIdentity.ts";
import {
noteCodexTurnStateProvenance,
readCodexTurnStateHeader,
} from "../../config/codexTurnState.ts";
import { HTTP_STATUS, STREAM_RECOVERY } from "../../config/constants.ts";
import { createRecoverableStream, makeContinuationBody } from "../../services/streamRecovery.ts";
import { persistCodexChildQuotaResponse } from "../../services/codexAccount/index.ts";
import { invalidateCodexQuotaCache } from "../../services/codexQuotaFetcher.ts";
import { invalidateGenericQuotaCacheOnStatus } from "../../services/genericQuotaFetcher.ts";
import { withRateLimit, resolveRequestQueueMaxWaitMs } from "../../services/rateLimitManager.ts";
import { acquireMany as acquireConcurrencyGates } from "../../services/accountSemaphore.ts";
import { rethrowAdmissionError, remainingQueueBudgetMs } from "./queueBudget.ts";
import { deduplicate } from "../../services/requestDedup.ts";
import {
classifyModelScope429,
getModelScopeRetryDelayMs,
} from "../../services/modelscopePolicy.ts";
import { incrementRequestCount } from "../../services/geminiRateLimitTracker.ts";
import { normalizeHeaders } from "../../utils/headers.ts";
import { runWithCapture } from "../../utils/providerRequestLogging.ts";
import type { Capture } from "../../utils/providerRequestLogging.ts";
import {
isStreamRecoveryExplicitlyConfigured,
resolveResilienceSettings,
type ResilienceSettings,
} from "@/lib/resilience/settings";
import { updatePendingScope, type PendingRequestScope } from "@/lib/usage/pendingRequestScope";
import { shouldIsolateProbeFailures } from "@/shared/utils/probeOrigin";
import { resolveProviderId } from "@/shared/constants/providers";
import { buildContinuationLogHooks } from "./recoveryTraceLogging.ts";
import { formatStreamRecoveryRetryWarning } from "./streamErrorResult.ts";
import { injectSystemPromptPostTranslation } from "../../services/systemPrompt.ts";
export type ChatCoreExecutorResult = {
response: Response;
url?: string;
transport?: string;
headers?: Record<string, string>;
body?: unknown;
_accountSemaphoreRelease?: () => void;
_executionCredentials?: Record<string, unknown>;
_dedupSnapshot?: {
status: number;
statusText: string;
headers: [string, string][];
payload: string;
};
[key: string]: unknown;
};
export type TrustedEffortContext = {
originModel?: string;
resolvedThinkingEffort?: string | null;
defaultThinkingEffort?: string | null;
};
type ChatLog =
| {
debug?: (...args: unknown[]) => void;
info?: (...args: unknown[]) => void;
warn?: (...args: unknown[]) => void;
error?: (...args: unknown[]) => void;
}
| null
| undefined;
export type ExecuteProviderRequestDeps = {
agentGoalPolicy: AgentGoalPolicy;
assertManagedLeaseFence: (attemptConnectionId: string | null | undefined) => void;
buildUpstreamHeadersForExecute: (modelToCall: string) => Record<string, string>;
clientRawRequest: {
headers?: Headers | Record<string, unknown> | null;
signal?: AbortSignal | null;
};
clientResponseFormat: string | null | undefined;
connectionId: string | null | undefined;
contextEditingEnabled?: boolean;
correlationId: string | null | undefined;
credentials: Record<string, unknown> | null | undefined;
dedupEnabled: boolean;
dedupHash: string | null;
effectiveModel: string;
executor: Pick<BaseExecutor, "execute">;
extendedContext?: boolean;
getExecutionCredentials: () => Record<string, unknown>;
getExecutorClientHeaders: () => Record<string, string>;
isModelScope: () => boolean;
isOpencodeClient: boolean;
log: ChatLog;
model: string;
onCredentialsRefreshed:
((next: Record<string, unknown>) => void | Promise<void>) | null | undefined;
pendingScope: PendingRequestScope;
provider: string;
providerRequestCapture: Capture;
rawBody: Record<string, unknown>;
recordKeyHealthStatus: (
status: number,
creds: Record<string, unknown> | null | undefined,
transport?: string,
failureDetail?: string
) => void;
requestedModel: string;
resilienceSettings: ResilienceSettings;
settings: Record<string, unknown> | null | undefined;
skipUpstreamRetry: boolean;
stream: boolean;
streamController: { signal: AbortSignal };
targetFormat: string;
trace: (label: string, extra?: Record<string, unknown>) => void;
traceId: string;
translatedBody: Record<string, unknown>;
trustedEffortContext: TrustedEffortContext;
upstreamStream: boolean;
userAgent: string | undefined;
};
export async function executeProviderRequest(
deps: ExecuteProviderRequestDeps,
modelToCall: string = deps.effectiveModel,
allowDedup: boolean = false
): Promise<ChatCoreExecutorResult> {
const {
agentGoalPolicy,
assertManagedLeaseFence,
buildUpstreamHeadersForExecute,
clientRawRequest,
clientResponseFormat,
connectionId,
contextEditingEnabled,
correlationId,
credentials,
dedupEnabled,
dedupHash,
executor,
extendedContext,
getExecutionCredentials,
getExecutorClientHeaders,
isModelScope,
isOpencodeClient,
log,
model,
onCredentialsRefreshed,
pendingScope,
provider,
providerRequestCapture,
rawBody,
recordKeyHealthStatus,
requestedModel,
resilienceSettings,
settings,
skipUpstreamRetry,
stream,
streamController,
targetFormat,
trace,
traceId,
translatedBody,
trustedEffortContext,
upstreamStream,
userAgent: _userAgent,
} = deps;
const body = rawBody;
const execute = async () => {
// Upstream body preparation extracted to chatCore/upstreamBody.ts (#3501 — first internal
// sub-slice of executeProviderRequest); produces the body sent upstream (payload rules +
// tool-limit truncation + prompt_cache_key injection).
let bodyToSend = await prepareUpstreamBody({
translatedBody,
modelToCall,
...trustedEffortContext,
provider,
targetFormat,
credentials: getExecutionCredentials(),
log,
bypassDefaultToolLimit: isOpencodeClient,
isOpencodeClient,
rawBody: body,
clientRawRequest,
});
// Global System Prompt — SINGLE injection point (post-translation) for
// carrier-ful targets. The old unconditional pre-translation pass
// (former chatCore injectSystemPrompt call) was removed: it chained
// with this pass to inject prefix/suffix 2-3x and dual-wrote
// body.system + messages[] on the claude path, which strict upstreams
// (HCP-Vision vLLM: "System message must be at the beginning") reject
// with 400. Format-aware via targetFormat: messages[] (openai/codex —
// prefix FIRST system, suffix LAST), claude `system` field, gemini
// `systemInstruction`, responses `instructions`. Carrier-less targets
// (kiro user-fold, antigravity Cloud Code envelope) are covered by the
// gated PRE-translation pass before translateRequest instead.
bodyToSend = injectSystemPromptPostTranslation(bodyToSend, { targetFormat });
updatePendingScope(pendingScope, {
providerRequest: bodyToSend,
stage: "payload_prepared",
});
let releaseRawResultAccountSemaphore = () => {};
try {
const rawResult: ChatCoreExecutorResult = await (async () => {
let attempts = 0;
const isModelScopeForRequest = isModelScope();
const maxAttempts = isModelScopeForRequest ? 3 : provider === "codex" ? 3 : 1;
while (attempts < maxAttempts) {
trace("pre_executor", { attempt: attempts });
updatePendingScope(pendingScope, {
stage: "sending_to_provider",
});
const execCreds = getExecutionCredentials();
const executionConnectionId = getExecutionConnectionId(execCreds);
const attemptConnectionId = executionConnectionId || connectionId;
const accountSemaphoreMaxConcurrency = resolveAccountSemaphoreMaxConcurrency(execCreds);
const accountSemaphoreKey = resolveAccountSemaphoreKey({
provider,
model: modelToCall,
connectionId: attemptConnectionId,
credentials: execCreds,
});
const canonicalProviderKey = resolveProviderId(String(provider).trim().toLowerCase());
const providerConcurrency =
resilienceSettings.providerQuotaOverrides[canonicalProviderKey]?.providerConcurrency ??
0;
trace("pre_semaphore", {
semaphoreKey: accountSemaphoreKey,
max: accountSemaphoreMaxConcurrency,
});
if (accountSemaphoreKey && accountSemaphoreMaxConcurrency != null) {
updatePendingScope(pendingScope, {
stage: "waiting_account_slot",
});
}
const maxWaitMs = resolveRequestQueueMaxWaitMs(
provider,
undefined,
attemptConnectionId ?? undefined
);
const gateStartedAt = Date.now();
const releaseAccountSemaphore = await acquireConcurrencyGates(
[
{
key: "global",
maxConcurrency: resilienceSettings.requestQueue.globalConcurrentRequests,
},
{
key: `provider:${canonicalProviderKey}`,
maxConcurrency: providerConcurrency,
},
{
key: accountSemaphoreKey || "",
maxConcurrency: accountSemaphoreKey ? accountSemaphoreMaxConcurrency : null,
},
],
{
timeoutMs: maxWaitMs,
maxQueueSize: resilienceSettings.requestQueue.maxQueueDepth,
signal: streamController.signal,
}
).catch(rethrowAdmissionError);
const remainingAfterGate = remainingQueueBudgetMs(maxWaitMs, gateStartedAt);
trace("post_semaphore", { maxWaitMs, remainingAfterGate });
updatePendingScope(pendingScope, {
stage: "waiting_rate_limit",
});
try {
trace("pre_rate_limit", { connectionId: attemptConnectionId });
const rawExecutorResult = await withRateLimit(
provider,
attemptConnectionId,
modelToCall,
async () => {
trace("inside_rate_limit", { connectionId: attemptConnectionId });
updatePendingScope(pendingScope, {
stage: "rate_limit_slot_acquired",
});
assertManagedLeaseFence(attemptConnectionId);
return executeWithUpstreamStartTimeout({
executor,
provider,
model: modelToCall,
connectionTimeoutMs: resolveConnectionTimeoutMs(execCreds?.providerSpecificData),
signal: streamController.signal,
log,
execute: (signal) =>
runWithCapture(providerRequestCapture, () =>
executor.execute({
model: modelToCall,
body: bodyToSend,
stream: upstreamStream,
credentials: execCreds,
signal,
log,
extendedContext,
upstreamExtraHeaders: buildUpstreamHeadersForExecute(modelToCall),
clientHeaders: getExecutorClientHeaders(),
clientResponseFormat,
onCredentialsRefreshed,
skipUpstreamRetry,
contextEditing: { enabled: contextEditingEnabled },
correlationId,
})
),
});
},
streamController.signal,
remainingAfterGate,
correlationId ?? undefined,
{
executor: executor as unknown as { getTimeoutMs?: () => unknown },
providerSpecificData: execCreds?.providerSpecificData,
}
);
const res = normalizeExecutorResult(rawExecutorResult);
trace("post_executor", { status: res?.response?.status });
// When a payload override rewrote body.model (custom-model alias →
// real upstream id, e.g. `gemini-3.7-flash-high` → `gemini-3.7-flash`),
// log and track the WIRE model so dashboards/telemetry reflect what
// actually shipped and Gemini rate-limit accounting uses the real id
// (the executor already built its URL from the same rewritten model).
const wireModel = typeof res.model === "string" && res.model ? res.model : modelToCall;
if (wireModel !== modelToCall) {
log?.debug?.(
"PAYLOAD_RULES",
`Payload rules rewrote model for URL: requested=${modelToCall} wire=${wireModel}`
);
}
if (
provider === "codex" &&
attemptConnectionId &&
!(await shouldIsolateProbeFailures())
) {
try {
const persistedQuota = await persistCodexChildQuotaResponse({
connectionId: String(attemptConnectionId),
model: modelToCall || model || requestedModel || "",
headers: normalizeHeaders(res.response.headers),
status: res.response.status,
});
if (persistedQuota) {
execCreds.providerSpecificData = persistedQuota.providerSpecificData;
if (persistedQuota.exhaustionLog) {
log?.debug?.("CODEX", persistedQuota.exhaustionLog);
}
}
if (res.response.status === 429) {
invalidateCodexQuotaCache(String(attemptConnectionId));
}
} catch (err) {
const errMessage = err instanceof Error ? err.message : String(err);
log?.debug?.("CODEX", `Failed to persist codex quota state: ${errMessage}`);
}
} else if (attemptConnectionId && res.response.status === 429) {
// Dropped generic quota cache after 429
invalidateGenericQuotaCacheOnStatus({
provider,
connectionId: String(attemptConnectionId),
status: res.response.status,
isolateProbe: await shouldIsolateProbeFailures(),
});
}
// Track Gemini RPM + RPD request counts for 429 classification
if (provider === "gemini") {
incrementRequestCount(wireModel);
}
updatePendingScope(pendingScope, {
stage: "provider_response_started",
});
if (
stream &&
(res.response.ok ||
res.response.status === HTTP_STATUS.UNAUTHORIZED ||
res.response.status === HTTP_STATUS.FORBIDDEN) &&
executionConnectionId &&
!(await shouldIsolateProbeFailures())
) {
const failureDetail = res.response.ok
? ""
: await res.response
.clone()
.text()
.catch(() => "");
recordKeyHealthStatus(res.response.status, execCreds, res.transport, failureDetail);
}
if (isModelScope() && res.response.status === 429 && attempts < maxAttempts - 1) {
const bodyPeek = await res.response
.clone()
.text()
.catch(() => "");
const normalizedHeaders = normalizeHeaders(res.response.headers);
const decision = classifyModelScope429(bodyPeek, normalizedHeaders);
if (decision.retryable) {
const delay = getModelScopeRetryDelayMs(normalizedHeaders, attempts);
log?.warn?.(
"MODELSCOPE_RETRY",
`429 ${decision.kind}; retrying in ${delay}ms (model remaining: ${decision.snapshot.modelRemaining ?? "unknown"})`
);
releaseAccountSemaphore();
await new Promise((r) => setTimeout(r, delay));
attempts++;
continue;
}
}
// For streaming: release the semaphore when the client drains or cancels the stream.
// Non-2xx streams must drop the slot before returning so the pipeline can rotate
// accounts without holding the failed connection's concurrency gate. Do NOT
// cancel() the body here — the pipeline clones it (BYOP 422 / toOutcome).
if (stream) {
const originalBody = res.response.body;
const okStatus = res.response.status >= 200 && res.response.status < 300;
if (!originalBody || !okStatus) {
releaseAccountSemaphore();
return {
...res,
_executionCredentials: execCreds,
};
}
// Opt-in transparent stream recovery (free-claude-code port, default OFF).
// Only engages for a successful (2xx) stream — an error body must never be
// held or replayed. Setting is read once here from the cached resolved
// resilience settings; the default path is byte-for-byte unchanged.
let streamRecoveryEnabled = false;
let continueMidStreamEnabled = false;
let throughputWatchdog =
resolveResilienceSettings(null).streamRecovery.throughputWatchdog;
if (okStatus) {
try {
// Reuse the request-consolidated settings read (see line ~2076) — no
// second DB/cache hit. Default OFF when the setting is absent.
const sr = resolveResilienceSettings(settings).streamRecovery;
// Fail-closed: the agent-goal-policy heuristic may only ADD recovery
// when the operator has no explicit configuration. If the operator
// explicitly configured stream recovery (env var or DB/settings
// override), that value always wins — the goal policy must never
// re-enable recovery the operator explicitly turned off.
const operatorExplicit = isStreamRecoveryExplicitlyConfigured(settings);
const goalOverride = !operatorExplicit && agentGoalPolicy.streamRecoveryEnabled;
streamRecoveryEnabled = sr.enabled || goalOverride;
continueMidStreamEnabled = sr.continueMidStream === true;
throughputWatchdog = sr.throughputWatchdog;
if (goalOverride && !sr.enabled) {
log?.info?.(
"AGENT_GOAL",
`agentGoalPolicy override: stream recovery enabled for goal request requestId=${traceId} model=${modelToCall || model || requestedModel || "unknown"}`
);
}
} catch {
streamRecoveryEnabled = false;
continueMidStreamEnabled = false;
throughputWatchdog =
resolveResilienceSettings(null).streamRecovery.throughputWatchdog;
}
}
let clientBody: ReadableStream<Uint8Array>;
if (streamRecoveryEnabled || throughputWatchdog.enabled) {
// Run the SAME upstream (same account/creds) with a given body and return
// its 2xx stream, or null. Used both by the early-retry re-open (same body)
// and the mid-stream continuation (assistant-prefilled body).
const runUpstreamStream = async (
body: unknown
): Promise<ReadableStream<Uint8Array> | null> => {
try {
assertManagedLeaseFence(attemptConnectionId);
const retryRaw = await executeWithUpstreamStartTimeout({
executor,
provider,
model: modelToCall,
connectionTimeoutMs: resolveConnectionTimeoutMs(
execCreds?.providerSpecificData
),
signal: streamController.signal,
log,
execute: (signal) =>
runWithCapture(providerRequestCapture, () =>
executor.execute({
model: modelToCall,
body,
stream: upstreamStream,
credentials: execCreds,
signal,
log,
extendedContext,
upstreamExtraHeaders: buildUpstreamHeadersForExecute(modelToCall),
clientHeaders: getExecutorClientHeaders(),
clientResponseFormat,
onCredentialsRefreshed,
skipUpstreamRetry,
contextEditing: { enabled: contextEditingEnabled },
correlationId,
})
),
});
const retryRes = normalizeExecutorResult(retryRaw);
const retryOk =
retryRes.response.status >= 200 && retryRes.response.status < 300;
if (retryOk && retryRes.response.body) {
return retryRes.response.body as ReadableStream<Uint8Array>;
}
await retryRes.response.body?.cancel().catch(() => {});
return null;
} catch {
return null;
}
};
// Mid-stream continuation (Fase 4.4): re-request with the partial text as an
// assistant prefill. Gated by its own setting and only for OpenAI-compatible
// bodies (makeContinuationBody returns null otherwise).
const continueStream = continueMidStreamEnabled
? (assistantSoFar: string) => {
const continuationBody = makeContinuationBody(
bodyToSend as Record<string, unknown>,
assistantSoFar
);
return continuationBody
? runUpstreamStream(continuationBody)
: Promise.resolve(null);
}
: undefined;
clientBody = createRecoverableStream(
originalBody as ReadableStream<Uint8Array>,
() => runUpstreamStream(bodyToSend),
{
finalize: releaseAccountSemaphore,
onRetry: (attempt, err) =>
log?.warn?.(
"STREAM_RECOVERY",
formatStreamRecoveryRetryWarning(
attempt,
STREAM_RECOVERY.EARLY_RETRY_MAX,
err
)
),
continueStream,
...buildContinuationLogHooks(log, correlationId),
throughputWatchdog,
onWatchdogAbort: () =>
log?.warn?.(
"STREAM_WATCHDOG",
"active upstream stream stayed below the configured useful-output rate"
),
}
);
} else {
clientBody = wrapReadableStreamWithFinalize(originalBody, releaseAccountSemaphore);
}
return {
...res,
_executionCredentials: execCreds,
response: new Response(clientBody, {
status: res.response.status,
statusText: res.response.statusText,
headers: new Headers(normalizeHeaders(res.response.headers)),
}),
};
}
return {
...res,
_executionCredentials: execCreds,
_accountSemaphoreRelease: releaseAccountSemaphore,
};
} catch (error) {
releaseAccountSemaphore();
throw error;
}
}
})();
if (stream) {
return rawResult;
}
// Non-stream: release semaphore immediately after reading full response body.
const status = rawResult.response.status;
releaseRawResultAccountSemaphore =
typeof rawResult._accountSemaphoreRelease === "function"
? rawResult._accountSemaphoreRelease
: () => {};
const statusText = rawResult.response.statusText;
const headersObj = normalizeHeaders(rawResult.response.headers);
const responseHeaders = new Headers(headersObj);
stripStaleForwardingHeaders(responseHeaders);
stripNextMiddlewareControlHeaders(responseHeaders);
// The upstream headers (turn-state included) are about to be committed
// to the client — record which connection minted the blob so a later
// cross-account echo can be stripped (Codex failover guard).
if (provider === "codex" && readCodexTurnStateHeader(responseHeaders)) {
noteCodexTurnStateProvenance(
getCodexClientSessionId(clientRawRequest?.headers),
rawResult._executionCredentials?.connectionId ?? credentials?.connectionId
);
}
const contentType = (responseHeaders.get("content-type") || "").toLowerCase();
const payload = await readNonStreamingResponseBody(
rawResult.response,
contentType,
upstreamStream
);
// Use the exact execution credential selected for this request. Model capability
// failures stay in routing telemetry; authoritative success only recovers this key.
if (
rawResult._executionCredentials?.connectionId &&
(rawResult._executionCredentials.apiKey || rawResult._executionCredentials.accessToken)
) {
recordKeyHealthStatus(
status,
rawResult._executionCredentials,
rawResult.transport,
status >= 400 ? payload : ""
);
}
releaseRawResultAccountSemaphore();
releaseRawResultAccountSemaphore = () => {};
return {
...rawResult,
response: new Response(payload, { status, statusText, headers: responseHeaders }),
_dedupSnapshot: {
status,
statusText,
headers: (() => {
const arr: [string, string][] = [];
responseHeaders.forEach((v, k) => arr.push([k, v]));
return arr;
})(),
payload,
},
};
} catch (error) {
releaseRawResultAccountSemaphore();
throw error;
}
};
if (allowDedup && dedupEnabled && dedupHash) {
const dedupResult = await deduplicate(dedupHash, execute);
if (dedupResult.wasDeduplicated) {
log?.debug?.("DEDUP", `Joined in-flight request hash=${dedupHash}`);
}
return materializeDeduplicatedExecutionResult(dedupResult.result);
}
return execute();
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,749 @@
import { getCodexClientSessionId } from "../../config/codexIdentity.ts";
import {
noteCodexTurnStateProvenance,
readCodexTurnStateHeader,
} from "../../config/codexTurnState.ts";
import {
STREAM_DISCONNECT_GRACE_PERIOD_MS,
STREAM_READINESS_MAX_TIMEOUT_MS,
STREAM_READINESS_TIMEOUT_MS,
STREAM_RECOVERY,
} from "../../config/constants.ts";
import { incrementTokenUsage } from "../../services/geminiRateLimitTracker.ts";
import { requestTtftMs, streamEmittedOutput } from "../../utils/streamTiming.ts";
import { clearPostOutputFailureStreak } from "../../services/accountFallback/postOutputFailureStreak.ts";
import {
createRoutingEvent,
emitRoutingEvent,
outcomeFromStatus,
} from "../../services/routing/index.ts";
import { FORMATS } from "../../translator/formats.ts";
import { needsTranslation } from "../../translator/index.ts";
import { runEmptyTurnRetryLoop } from "./emptyTurnRetryLoop.ts";
import { buildErrorBody } from "../../utils/error.ts";
import {
createPassthroughStreamWithLogger,
createSSETransformStreamWithLogger,
} from "../../utils/stream.ts";
import * as streamFailure from "../../utils/streamFailureFinalization.ts";
import { ensureStreamReadiness } from "../../utils/streamReadiness.ts";
import { resolveStreamReadinessTimeout } from "../../utils/streamReadinessPolicy.ts";
import { maybeFallbackAfterReadiness } from "./streamReadinessFallback.ts";
import { captureStreamReasoningForReplay } from "./streamReasoningCapture.ts";
import { meteredBudgetCost } from "@/lib/usage/meteredBudgetPolicy";
import { resolveSuppressThinkClose } from "../../utils/thinkCloseMarker.ts";
import { hasActiveClaudeThinking } from "../../utils/thinkingBudget.ts";
import { buildCacheUsageLogMeta } from "./cacheUsageMeta.ts";
import { recordContextEditingTelemetryHook } from "./contextEditingTelemetry.ts";
import { readCpaAuthIndex } from "./failureUsage.ts";
import { emitRequestGamificationEvent } from "./gamificationEvent.ts";
import { maybeConvertJsonBodyToSse } from "./jsonBodyToSse.ts";
import { runMemoryExtractionGate } from "./memoryExtraction.ts";
import { mergeResponseToolNameMap } from "./passthroughToolNames.ts";
import { runPluginOnResponseHook, runPluginOnStreamCompleteHook } from "./pluginOnResponse.ts";
import { routingFinishReason } from "./routingFinishReason.ts";
import { wrapReadableStreamWithFinalize } from "./streamFinalize.ts";
import { buildStreamLedgerDetails, recordStreamingCost } from "./streamingCost.ts";
import { assembleStreamingPipeline } from "./streamingPipeline.ts";
import { scheduleStreamingQuotaShareConsumption } from "./streamingQuotaShare.ts";
import { assembleStreamingResponseHeaders } from "./streamingResponseHeaders.ts";
import { storeStreamingSemanticCacheResponse } from "./streamingSemanticCacheStore.ts";
import { recordStreamingUsageStats } from "./streamingUsageStats.ts";
import { maybeSyncClaudeExtraUsageState } from "./telemetryHelpers.ts";
import { getExecutorTimeoutMs, resolveConnectionTimeoutMs } from "./upstreamTimeouts.ts";
import { recordCost } from "@/domain/costRules";
import { extractFacts } from "@/lib/memory/extraction";
import { calculateCost } from "@/lib/usage/costCalculator";
import { appendRequestLog, trackPendingRequest } from "@/lib/usageDb";
import { isFeatureFlagEnabled } from "@/shared/utils/featureFlags.ts";
import { getProviderCredentials } from "@/sse/services/auth";
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- deps bag mirrors the barrel closure
type Loose = any;
export type StreamingTailDeps = Record<string, Loose>;
export async function runStreamingTail(deps: StreamingTailDeps) {
const {
agentGoalPolicy,
apiKeyInfo,
attachCompressionUsageReceiptAfterAnalytics,
body,
bodyForCacheWrite,
claudePromptCacheLogMeta,
clientRawRequest,
clientResponseFormat,
comboStrategy,
compressionResponseMeta,
connectionId,
contextEditingEnabled,
copilotCompatibleReasoning,
correlationId,
createPiiTransform,
credentials,
currentModel,
customToolNames,
echoModel,
endpointPath,
executeProviderRequest,
executor,
fallbackAttempts,
getCurrentConnectionId,
forcedConnectionId,
isCombo,
isDroidCLI,
isResponsesEndpoint,
log,
managedLease,
memoryOwnerId,
memorySettings,
model,
modelInfo,
onRequestSuccess,
onStreamFailure,
pendingConnId,
pendingRequestId,
persistAttemptLogs,
persistFailureUsage,
pipelineSessionId,
provider,
providerHeaders,
providerRequestCapture,
providerUrl,
reasoningCacheScope,
reasoningReplayHistory,
releaseTurnExecution,
reqLogger,
requestToolIdentityMap,
resolveReportedServiceTier,
semanticCacheEnabled,
skillRequestId,
startTime,
stream,
streamController,
streamUserAgent,
targetFormat,
thinkingMarkerHeader,
toolNameMap,
traceId,
translatedBody,
videoBridgeObserved,
} = deps;
let providerResponse, finalBody, effectiveServiceTier;
providerResponse = deps.providerResponse;
finalBody = deps.finalBody;
effectiveServiceTier = deps.effectiveServiceTier;
let onPipelineStreamError = null;
let onClientDisconnectFinalize = null;
let turnExecutionHandedOffToStream = false;
// Streaming response
// #3089 — some "reasoning" openai-compatible upstreams ignore a stream:true
// request and return a complete application/json chat-completion body instead
// of an SSE stream. The readiness check below only recognizes SSE `data:`
// frames, so that body produced a spurious STREAM_EARLY_EOF / HTTP 502 even
// though it carried valid content/reasoning_content. Detect a JSON (non-SSE)
// upstream body and synthesize an equivalent OpenAI SSE stream so the
// streaming pipeline (and the client) get a valid stream.
providerResponse = await maybeConvertJsonBodyToSse(providerResponse, { log, provider, model });
const streamReadinessPolicy = resolveStreamReadinessTimeout({
baseTimeoutMs: STREAM_READINESS_TIMEOUT_MS,
provider,
model,
body: (finalBody || translatedBody) as Record<string, unknown> | null | undefined,
sourceBody: body as Record<string, unknown> | null | undefined,
maxTimeoutMs: agentGoalPolicy.detected
? Math.max(STREAM_READINESS_MAX_TIMEOUT_MS, agentGoalPolicy.readinessMaxTimeoutMs)
: STREAM_READINESS_MAX_TIMEOUT_MS,
cascadeTimeoutMs: getExecutorTimeoutMs(
executor,
provider,
model,
resolveConnectionTimeoutMs(credentials?.providerSpecificData)
),
});
if (streamReadinessPolicy.timeoutMs !== streamReadinessPolicy.baseTimeoutMs) {
log?.debug?.(
"STREAM",
`adaptive readiness timeout=${streamReadinessPolicy.timeoutMs}ms base=${streamReadinessPolicy.baseTimeoutMs}ms reason=${streamReadinessPolicy.reasons.join(",")}`
);
}
let streamReadiness = await ensureStreamReadiness(providerResponse, {
timeoutMs: streamReadinessPolicy.timeoutMs,
maxTimeoutMs: streamReadinessPolicy.maxTimeoutMs,
provider,
model,
log,
});
// A stall is an upstream issue, not an account fault — the executor loop
// already ended at headers, so this bounded retry is the only recovery left.
const fallback = await maybeFallbackAfterReadiness({
streamReadiness,
clientAborted: streamController.signal.aborted,
failedConnectionId: getCurrentConnectionId(),
failedBody: providerResponse,
currentModel,
streamReadinessPolicy,
provider,
model,
log,
reqLogger,
providerUrl,
providerHeaders,
finalBody,
translatedBody,
executeProviderRequest,
providerRequestCapture,
});
streamReadiness = fallback.readiness;
providerResponse = fallback.providerResponse;
finalBody = fallback.finalBody;
if (streamReadiness.ok === false) {
const { response: failureResponse, reason } = streamReadiness;
const { classificationReason, upstreamDiagnostic } = streamReadiness;
trackPendingRequest(model, provider, pendingConnId, false, undefined, pendingRequestId);
appendRequestLog({
model,
provider,
connectionId,
status: `FAILED ${failureResponse.status}`,
}).catch(() => {});
persistAttemptLogs({
status: failureResponse.status,
error: reason,
providerRequest: finalBody || translatedBody,
clientResponse: buildErrorBody(
failureResponse.status,
classificationReason,
upstreamDiagnostic ? { error: { message: upstreamDiagnostic } } : undefined
),
claudeCacheMeta: claudePromptCacheLogMeta,
cacheSource: "upstream",
});
persistFailureUsage(failureResponse.status, streamReadiness.code);
// Do NOT call onStreamFailure — a stream stall is an upstream issue,
// not an account/quota failure. Marking the account unavailable here
// would lock out legitimate accounts when the upstream hangs.
return {
result: {
success: false,
status: failureResponse.status,
error: reason,
classificationError: classificationReason,
errorType: streamReadiness.type,
errorCode: streamReadiness.code,
response: failureResponse,
},
carry: { onPipelineStreamError, onClientDisconnectFinalize, turnExecutionHandedOffToStream },
};
}
providerResponse = streamReadiness.response;
// Flush-empty retry (opt-in `FLUSH_EMPTY_RETRY_ENABLED`, default off): when the
// upstream turn carries no usable content (reasoning-only 200, or a
// zero-valuable-chunk turn that the empty-stream guard would turn into a 502),
// issue bounded retries through the normal credential path BEFORE anything is
// exposed to the client — in particular before `onRequestSuccess` below.
// Empty turns are stochastic upstream misses, not account faults, so no
// cooldown and no forced exclusion: the round-robin picker may rotate
// fingerprint slots opportunistically, a single slot simply replays the same
// account. Budget: `STREAM_RECOVERY.EMPTY_TURN_RETRY_MAX` retries, then fall
// back to the current behavior. Translate-path streams only (mirror of the
// empty-stream guard); flag off = byte-for-byte unchanged. Bounded reader
// (abandon past the cap, never a full `text()` read); the original
// reconstructed response is piped, only the bounded copy is classified.
// Known TTFT cost when armed: a small valid turn under the cap is fully
// buffered before the first client byte (flag off by default, so the
// streaming path is untouched unless opted in).
if (stream && providerResponse.ok && providerResponse.body) {
let flushEmptyRetryArmed = false;
try {
flushEmptyRetryArmed = isFeatureFlagEnabled("FLUSH_EMPTY_RETRY_ENABLED");
} catch {
flushEmptyRetryArmed = false;
}
const isTranslatePath =
targetFormat === FORMATS.OPENAI_RESPONSES ||
needsTranslation(targetFormat, clientResponseFormat);
if (flushEmptyRetryArmed && isTranslatePath) {
const retried = await runEmptyTurnRetryLoop({
providerResponse,
credentials,
provider,
currentModel,
model,
targetFormat,
clientResponseFormat,
isAborted: () => clientRawRequest?.signal?.aborted === true,
timeoutMs: streamReadinessPolicy.timeoutMs,
maxTimeoutMs: streamReadinessPolicy.maxTimeoutMs,
maxRetries: STREAM_RECOVERY.EMPTY_TURN_RETRY_MAX,
translatedBody,
finalBody,
providerUrl,
providerHeaders,
correlationId,
traceId,
log,
getProviderCredentials,
routing: { leased: Boolean(managedLease), forcedConnectionId, apiKey: apiKeyInfo },
executeProviderRequest,
logTargetRequest: (url, headers, body) => reqLogger.logTargetRequest(url, headers, body),
captureBody: (body) => providerRequestCapture.body(body),
});
providerResponse = retried.providerResponse;
if (retried.adopted) finalBody = retried.finalBody;
}
}
// Notify success - caller can clear error status if needed
if (onRequestSuccess) {
await onRequestSuccess();
}
const responseHeaders = assembleStreamingResponseHeaders({
providerHeaders: providerResponse.headers,
provider,
model,
pendingRequestId,
compressionResponseMeta,
comboStrategy,
fallbackAttempts,
isCombo, // #14116: foreign-account quota-header strip (only meaningful when true)
requestedConnectionId: forcedConnectionId || null,
selectedConnectionId: credentials?.connectionId ?? null,
});
// The streaming headers (turn-state included, when present) are committed to
// the client from here on — record which connection minted the blob so a
// later cross-account echo can be stripped (Codex failover guard). The
// in-place failover update means `credentials` is the winning account.
if (provider === "codex" && readCodexTurnStateHeader(providerResponse.headers)) {
noteCodexTurnStateProvenance(
getCodexClientSessionId(clientRawRequest?.headers),
credentials?.connectionId
);
}
// Create transform stream with logger for streaming response
let transformStream;
const responseToolNameMap = mergeResponseToolNameMap(
toolNameMap,
(finalBody as Record<string, unknown> | null | undefined) ?? null
);
let streamCompletionRecorded = false;
let streamFailureCompletionRecorded = false;
// Callback to save call log when stream completes (include responseBody when provided by stream)
let streamTimingOriginOffsetMs: number | null = null; // startTime → StreamTiming start
const onStreamComplete = ({
status: streamStatus,
usage: streamUsage,
responseBody: streamResponseBody,
providerPayload,
clientPayload,
reasoningMeta: streamReasoningMeta,
error: streamError,
errorCode: streamErrorCode,
firstOutputMs,
itlMs: streamItlMs,
interrupted: _streamInterrupted,
}) => {
const ttft = requestTtftMs(streamTimingOriginOffsetMs, firstOutputMs);
const normalizedStreamStatus = streamStatus || 200;
if (streamCompletionRecorded) return;
streamCompletionRecorded = true;
if (normalizedStreamStatus !== 200) {
if (streamFailureCompletionRecorded) return;
streamFailureCompletionRecorded = true;
}
const cacheUsageLogMeta = buildCacheUsageLogMeta(streamUsage);
const streamConnectionId = getCurrentConnectionId();
if (normalizedStreamStatus === 200) {
clearPostOutputFailureStreak(provider, streamConnectionId, modelInfo.model);
void maybeSyncClaudeExtraUsageState({
provider,
connectionId: streamConnectionId,
providerSpecificData: credentials?.providerSpecificData,
log,
});
}
if (normalizedStreamStatus === 200 && streamResponseBody) {
captureStreamReasoningForReplay({
streamResponseBody,
clientResponseFormat,
responseToolNameMap,
providerRequestBody: finalBody || translatedBody || body,
translatedBody,
reasoningReplayHistory,
provider,
model,
reasoningCacheScope,
videoTranscriptSensitive: videoBridgeObserved,
});
}
effectiveServiceTier = resolveReportedServiceTier(streamResponseBody) ?? effectiveServiceTier;
// Context Editing telemetry (streaming): the reconstructed stream body now carries
// context_management.applied_edits from the final message_delta snapshot. Mirror the
// non-streaming hook so streaming context-clear savings also surface under engine
// "context-editing" in compression analytics. Best-effort, Claude-only.
if (normalizedStreamStatus === 200) {
recordContextEditingTelemetryHook({
contextEditingEnabled,
provider,
responseBody: streamResponseBody,
skillRequestId,
log,
});
}
streamFailure.finalizeStreamRequestLog({
pendingRequestId,
model,
provider,
connectionId: streamConnectionId,
providerResponse: providerPayload ?? streamResponseBody ?? undefined,
clientResponse: clientPayload ?? streamResponseBody ?? undefined,
status: normalizedStreamStatus,
error: streamError,
errorCode: streamErrorCode,
});
// Track cache token metrics for streaming responses
if (streamUsage && typeof streamUsage === "object") {
attachCompressionUsageReceiptAfterAnalytics(streamUsage as Record<string, unknown>, "stream");
// Track Gemini token consumption for TPM rate-limit pre-check
if (provider === "gemini") {
const promptTokens =
typeof (streamUsage as Record<string, unknown>).prompt_tokens === "number"
? ((streamUsage as Record<string, unknown>).prompt_tokens as number)
: 0;
if (promptTokens > 0) incrementTokenUsage(model, promptTokens);
}
}
recordStreamingUsageStats(streamUsage, {
provider,
model,
streamStatus: normalizedStreamStatus,
startTime,
ttft,
streamErrorCode,
connectionId: streamConnectionId,
apiKeyInfo,
effectiveServiceTier,
isCombo,
comboStrategy,
endpoint: endpointPath,
cpaAuthIndex: readCpaAuthIndex(providerResponse),
});
// Routing event (feedback foundation) — fire-and-forget, cheap, never blocks
// the stream. Feeds the quality tracker + optional OTel exporter.
void emitRoutingEvent(
createRoutingEvent({
requestId: traceId || pendingRequestId || "unknown",
provider: provider || "unknown",
model: model || "unknown",
strategy: isCombo ? (comboStrategy ?? "combo") : "direct",
latencyMs: Date.now() - startTime,
ttftMs: typeof ttft === "number" && Number.isFinite(ttft) && ttft >= 0 ? ttft : null,
itlMs:
typeof streamItlMs === "number" && Number.isFinite(streamItlMs) && streamItlMs >= 0
? streamItlMs
: null,
inputTokens:
streamUsage && typeof streamUsage === "object"
? (() => {
const promptTokens = (streamUsage as Record<string, unknown>).prompt_tokens;
return typeof promptTokens === "number" && Number.isFinite(promptTokens)
? promptTokens
: null;
})()
: null,
outputTokens:
streamUsage && typeof streamUsage === "object"
? (() => {
const completionTokens = (streamUsage as Record<string, unknown>).completion_tokens;
return typeof completionTokens === "number" && Number.isFinite(completionTokens)
? completionTokens
: null;
})()
: null,
cost: null,
retries: 0,
fallbackUsed: false, // combo-level fallback tracked by decisionTrace
outcome:
normalizedStreamStatus === 200
? "success"
: streamErrorCode === "stream_interrupted" || streamErrorCode === "aborted"
? "stream_interrupted"
: outcomeFromStatus(normalizedStreamStatus),
status: normalizedStreamStatus,
finishReason: routingFinishReason(streamResponseBody),
connectionId: streamConnectionId ?? credentials?.connectionId ?? null,
})
);
persistAttemptLogs({
status: normalizedStreamStatus,
error: streamError || undefined,
tokens: streamUsage || {},
responseBody: streamResponseBody ?? undefined,
providerRequest: finalBody || translatedBody,
providerResponse: providerPayload,
clientResponse: clientPayload ?? streamResponseBody ?? undefined,
claudeCacheMeta: claudePromptCacheLogMeta,
claudeCacheUsageMeta: cacheUsageLogMeta,
cacheSource: "upstream",
// #13130: persist TTFT so call_logs.ttft_ms lets the dashboard compute
// generation-time TPS instead of wall-clock TPS.
ttft,
reasoningMeta: streamReasoningMeta ?? null,
});
recordStreamingCost({
apiKeyId: apiKeyInfo?.id,
provider,
model,
streamUsage,
serviceTier: effectiveServiceTier,
calculateCost,
// Only the budget-consumable share may draw down the allowance.
recordCost: (apiKeyId, cost, details) => {
const budgetCost = meteredBudgetCost(provider, cost);
if (budgetCost > 0) recordCost(apiKeyId, budgetCost, details);
},
ledger: buildStreamLedgerDetails(effectiveServiceTier, normalizedStreamStatus < 400, traceId),
});
// === Quota Share POST-hook streaming (B/F7) — fire-and-forget, fail-open ===
// Resolve the real per-request cost (calculateCost) so USD-unit pools accrue
// on streaming traffic too; this previously recorded usd:0 hardcoded, which
// meant DeepSeek-style `usd/monthly` shared pools never blocked on streams.
scheduleStreamingQuotaShareConsumption({
apiKeyId: apiKeyInfo?.id,
connectionId: credentials?.connectionId,
provider,
model,
streamUsage,
streamStatus: normalizedStreamStatus,
serviceTier: effectiveServiceTier,
calculateCost,
log,
});
// === /Quota Share POST-hook streaming ===
if (streamStatus === 200) {
// #12150 P1b surface 3 (fix round 1): see the matching non-streaming
// gate above — an observed request populates NO durable memory from
// either the request-derived text or this streamed response.
runMemoryExtractionGate({
memoryOwnerId,
memorySettings,
videoBridgeObserved,
pipelineSessionId,
requestBody: body as Record<string, unknown>,
responseBody: (streamResponseBody ?? null) as Record<string, unknown> | null,
extractFacts,
log,
});
}
// Semantic cache: store assembled streaming response for future cache hits
storeStreamingSemanticCacheResponse({
enabled: semanticCacheEnabled,
streamStatus,
streamResponseBody,
body: bodyForCacheWrite,
headers: clientRawRequest?.headers,
model,
provider,
apiKeyId: apiKeyInfo?.id ?? undefined,
streamUsage,
log,
videoTranscriptSensitive: videoBridgeObserved,
});
// Plugin onStreamComplete hook — fire-and-forget, fail-open (#9571)
// Pass traceId as requestId so plugins can correlate the stream-completion event
// with the originating request (the same id used for onRequest/onResponse). (#11825)
runPluginOnStreamCompleteHook({
status: normalizedStreamStatus,
usage: streamUsage as Record<string, unknown> | undefined,
ttft,
model,
provider,
errorCode: streamErrorCode,
startTime,
requestId: traceId,
});
};
const streamFailureFinalizers = streamFailure.createStreamFailureFinalizers({
isFailureCompletionRecorded: () => streamFailureCompletionRecorded,
isStreamCompletionRecorded: () => streamCompletionRecorded,
onStreamComplete,
persistFailureUsage,
onStreamFailure,
hasEmittedOutput: () => streamEmittedOutput(transformStream),
});
const handleStreamFailure = streamFailureFinalizers.handleStreamFailure;
onPipelineStreamError = streamFailureFinalizers.onPipelineStreamError;
// #9653: gives a genuine, race-delayed completion a chance to land (see
// createClientDisconnectGraceHandler's doc comment) before persisting a false
// 499/0-tokens for a request that actually delivered its full response.
onClientDisconnectFinalize = streamFailure.createClientDisconnectGraceHandler({
isStreamCompletionRecorded: () => streamCompletionRecorded,
gracePeriodMs: STREAM_DISCONNECT_GRACE_PERIOD_MS,
finalize: (event) =>
handleStreamFailure({
status: 499,
message: `Client disconnected: ${event.reason}`,
code: "client_disconnected",
type: "client_disconnected",
}),
});
// For providers using Responses API format, translate stream back to openai (Chat Completions) format
// UNLESS client is Droid CLI which expects openai-responses format back
const needsResponsesTranslation =
targetFormat === FORMATS.OPENAI_RESPONSES &&
clientResponseFormat === FORMATS.OPENAI &&
!isResponsesEndpoint &&
!isDroidCLI;
const streamStateBody = finalBody || body;
// Client's explicit thinking intent (Anthropic Messages shape). Claude Code
// sends `{type:"enabled"}` or `{type:"adaptive"}` to opt into relaying
// upstream reasoning_content as Claude thinking blocks; `{type:"disabled"}`
// or an omitted `thinking` field opts out. Kept false for every other
// client schema (OpenAI / Responses), which never express intent through
// `body.thinking`. Mirrors hasActiveClaudeThinking() so the request and
// response sides agree on what counts as "thinking requested" — a prior
// inline `=== "enabled"` check silently suppressed `adaptive` (the intent
// Claude Code actually sends), leaking the mismatch as a broken tool-call
// turn (call log 1787566395384-bab9ab: reasoning dropped → model emitted
// DSML tool-call markers as plain text → incomplete `stop` finish).
const requestedThinking = hasActiveClaudeThinking((body ?? {}) as Record<string, unknown>);
streamTimingOriginOffsetMs = Date.now() - startTime;
if (needsResponsesTranslation) {
// Provider returns openai-responses, translate to openai (Chat Completions) that clients expect
log?.debug?.("STREAM", `Responses translation mode: openai-responses → openai`);
transformStream = createSSETransformStreamWithLogger(
"openai-responses",
"openai",
provider,
reqLogger,
responseToolNameMap,
model,
connectionId,
streamStateBody,
onStreamComplete,
apiKeyInfo,
handleStreamFailure,
copilotCompatibleReasoning,
false,
requestedThinking,
customToolNames,
// openai-responses → openai translation still wants the namespace identity
// map for #7936-style round-trip closure when the client also speaks
// Responses (Codex CLI).
requestToolIdentityMap
);
} else if (needsTranslation(targetFormat, clientResponseFormat)) {
// Standard translation for other providers
log?.debug?.("STREAM", `Translation mode: ${targetFormat} → ${clientResponseFormat}`);
transformStream = createSSETransformStreamWithLogger(
targetFormat,
clientResponseFormat,
provider,
reqLogger,
responseToolNameMap,
model,
connectionId,
streamStateBody,
onStreamComplete,
apiKeyInfo,
handleStreamFailure,
copilotCompatibleReasoning,
// Suppress the `</think>` close marker for clients that render it verbatim
// (e.g. OpenCode by UA; any client via `x-omniroute-thinking-marker: off`);
// preserved for Claude Code / Cursor and unknown clients by default (#5245 /
// #5312). Responses API clients always suppress it (structured reasoning
// items make the marker meaningless); otherwise the header wins over the
// UA allowlist.
resolveSuppressThinkClose({
userAgent: streamUserAgent,
thinkingMarkerHeader,
clientResponseFormat,
}),
requestedThinking,
customToolNames,
requestToolIdentityMap
);
} else {
log?.debug?.("STREAM", `Standard passthrough mode`);
transformStream = createPassthroughStreamWithLogger(
provider,
reqLogger,
responseToolNameMap,
model,
connectionId,
streamStateBody,
onStreamComplete,
apiKeyInfo,
handleStreamFailure,
clientResponseFormat,
requestToolIdentityMap
);
}
const finalStream = assembleStreamingPipeline({
providerResponse,
transformStream,
streamController,
createPiiTransform,
clientRawRequestHeaders: clientRawRequest?.headers,
clientResponseFormat,
echoModel,
responseHeaders,
// Same adaptive budget the pre-handoff readiness gate above just used —
// reasoning models that legitimately take a while to say anything keep
// that same patience for their first REAL content, not just their first
// lifecycle frame. See pipeWithDisconnect's own doc comment.
contentStallTimeoutMs: streamReadinessPolicy.timeoutMs,
});
const clientFacingStream = wrapReadableStreamWithFinalize(finalStream, releaseTurnExecution);
// ── Gamification event (fire-and-forget) ──
await emitRequestGamificationEvent({ apiKeyId: apiKeyInfo?.id, model, provider });
// ── Plugin onResponse hook (fire-and-forget) ──
await runPluginOnResponseHook({
requestId: traceId,
body,
model,
provider,
apiKeyInfo,
headers: clientRawRequest?.headers,
response: { status: 200, streamed: true },
});
const response = new Response(clientFacingStream, {
headers: responseHeaders,
});
turnExecutionHandedOffToStream = true;
return {
result: {
success: true,
response,
},
carry: { onPipelineStreamError, onClientDisconnectFinalize, turnExecutionHandedOffToStream },
};
}

View file

@ -131,17 +131,17 @@
"electron:build:mac": "npm run build && cd electron && npm run build:mac",
"electron:build:linux": "npm run build && cd electron && npm run build:linux",
"electron:smoke:packaged": "node scripts/dev/smoke-electron-packaged.mjs",
"test": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"test:unit": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"test:unit:ci": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"test:unit:ci:shard": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=$TEST_SHARD tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=$TEST_SHARD \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=$TEST_SHARD \"tests/unit/serial/**/*.test.ts\"",
"test:unit:fast": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"test": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"test:unit": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"test:unit:ci": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"test:unit:ci:shard": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=$TEST_SHARD tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=$TEST_SHARD \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=$TEST_SHARD \"tests/unit/serial/**/*.test.ts\"",
"test:unit:fast": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"test:scoped": "bash scripts/quality/test-scoped.sh",
"test:scoped:staged": "bash scripts/quality/test-scoped.sh --staged",
"test:scoped:full": "bash scripts/quality/test-scoped.sh --full",
"test:unit:shard": "concurrently --kill-others-on-fail -n s1,s2 \"npm:test:unit:shard:1\" \"npm:test:unit:shard:2\"",
"test:unit:shard:1": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=1/2 \"tests/unit/serial/**/*.test.ts\"",
"test:unit:shard:2": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=2/2 \"tests/unit/serial/**/*.test.ts\"",
"test:unit:shard:1": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=1/2 \"tests/unit/serial/**/*.test.ts\"",
"test:unit:shard:2": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 \"tests/unit/dashboard/**/*.test.ts\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=2/2 \"tests/unit/serial/**/*.test.ts\"",
"test:bun:db": "bun test tests/unit/db-adapters/bunSqliteAdapter.test.ts tests/unit/db-adapters/driverFactory.test.ts tests/unit/db-adapters/cliSqlite.test.mjs",
"test:plan3": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/plan3-p0.test.ts",
"test:fixes": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/fixes-p1.test.ts",
@ -294,7 +294,7 @@
"release:contributors": "node scripts/release/gen-contributors.mjs",
"release:uncovered": "node scripts/release/list-uncovered-commits.mjs",
"release:reconcile": "node scripts/release/reconcile-changelog.mjs",
"test:coverage:runner": "node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true NODE_OPTIONS=--max-old-space-size=8192 c8 --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 60 --lines 60 --functions 60 --branches 60 node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"test:coverage:runner": "node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true NODE_OPTIONS=--max-old-space-size=8192 c8 --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 60 --lines 60 --functions 60 --branches 60 node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 \"tests/unit/dashboard/**/*.test.ts\" && npm run test:unit:serial",
"test:unit:serial": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 \"tests/unit/serial/**/*.test.ts\"",
"alibaba:sync-allowlist": "node --import tsx/esm scripts/ops/sync-alibaba-allowlist.mjs",
"check:vitest-exclusions": "node scripts/check/check-vitest-exclusions.mjs",

View file

@ -58,7 +58,7 @@ export const COLLECTORS = [
// abaixo). Subdir novo: adicione aqui E nos scripts (o drift-check + o gate de
// órfãos forçam a manutenção em sincronia).
{
glob: "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts",
glob: "tests/unit/{api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts",
sources: ["package.json"],
},
// Node native runner — tests/unit/dashboard/** roda numa invocação separada com o hook

View file

@ -0,0 +1,400 @@
#!/usr/bin/env node
// scripts/dev/diff-chatcore-leaves.mjs
//
// Region diff that proves the four response-path leaves in open-sse/handlers/chatCore/ are
// mechanical lifts of the monolithic handleChatCore on the base ref.
//
// For every leaf it (1) slices the lifted region out of the BASE chatCore.ts (region.base:
// startPattern/endPattern), (2) slices the corresponding body out of the leaf (region.leaf), (3)
// tokenizes both with the TypeScript scanner (comments, whitespace and prettier re-wrap noise
// such as trailing commas vanish), (4) runs a Myers token diff and groups the edits into hunks.
// A hunk is "mechanical" only when it is one of the documented lift seams (see classifyHunk):
// the syncExecuteTranslatedBody(translatedBody) re-sync, the `return X` ->
// `return { response: X, carry: {...} }` wrapping with the exact per-leaf carry keys, and the
// region-boundary tokens that stay behind in handleChatCore. Every other hunk is printed as
// NON-MECHANICAL and the script exits 1 (exit 2 = a region could not be extracted).
//
// Usage: node scripts/dev/diff-chatcore-leaves.mjs [--base <git-ref>] [--verbose]
// default base: origin/release/v3.8.52 (the pre-split chatCore.ts that the leaves lift from).
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
const require = createRequire(import.meta.url);
const ts = require("typescript");
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const argv = process.argv.slice(2);
const flag = (name) => argv.includes(name);
const opt = (name, fallback) => (argv.includes(name) ? argv[argv.indexOf(name) + 1] : fallback);
const BASE_REF = opt("--base", "origin/release/v3.8.52");
const VERBOSE = flag("--verbose");
const BASE_FILE = "open-sse/handlers/chatCore.ts";
/** Normalize a source slice to comparable lines (drops blanks and comment-only lines). */
export function normalize(text) {
const out = [];
let inBlock = false;
for (const raw of text.split("\n")) {
let line = raw.trim();
if (inBlock) {
if (line.includes("*/")) inBlock = false;
continue;
}
if (line.startsWith("/*")) {
if (!line.includes("*/")) inBlock = true;
continue;
}
if (line === "" || line.startsWith("//")) continue;
line = line.replace(/\s+/g, " ");
// prettier re-wraps change trailing commas / semicolons-only differences after dedent
out.push(line);
}
return out;
}
/**
* Slice a region out of NORMALIZED lines. The region starts at the first `startPattern` match
* (searched after the first `after` match when given) and ends at the first `endPattern` match
* after the start (the LAST match when `endMode: "last"`). The end line is excluded unless
* `inclusiveEnd`.
*/
export function sliceRegion(
lines,
{ after, startPattern, endPattern, endMode = "first", inclusiveEnd }
) {
let from = 0;
if (after) {
from = lines.findIndex((l) => after.test(l));
if (from < 0) throw new Error(`anchor ${after} not found`);
}
let s = -1;
for (let i = from; i < lines.length; i++) {
if (startPattern.test(lines[i])) {
s = i;
break;
}
}
if (s < 0) throw new Error(`start pattern ${startPattern} not found`);
let e = -1;
if (endMode === "last") {
for (let i = lines.length - 1; i > s; i--) {
if (endPattern.test(lines[i])) {
e = i;
break;
}
}
} else {
for (let i = s + 1; i < lines.length; i++) {
if (endPattern.test(lines[i])) {
e = i;
break;
}
}
}
if (e < 0) throw new Error(`end pattern ${endPattern} not found after the start`);
return lines.slice(s, inclusiveEnd ? e + 1 : e);
}
/** Tokenize TypeScript source (comments/whitespace dropped, prettier-wrap noise removed). */
export function tokenize(text) {
const scanner = ts.createScanner(ts.ScriptTarget.ES2022, true, ts.LanguageVariant.Standard, text);
const tokens = [];
const stack = []; // "tpl" | "brace"
let prevKind = ts.SyntaxKind.Unknown;
const exprEnd = new Set([
ts.SyntaxKind.Identifier,
ts.SyntaxKind.CloseParenToken,
ts.SyntaxKind.CloseBracketToken,
ts.SyntaxKind.NumericLiteral,
ts.SyntaxKind.StringLiteral,
ts.SyntaxKind.NoSubstitutionTemplateLiteral,
ts.SyntaxKind.TemplateTail,
ts.SyntaxKind.RegularExpressionLiteral,
ts.SyntaxKind.ThisKeyword,
ts.SyntaxKind.TrueKeyword,
ts.SyntaxKind.FalseKeyword,
ts.SyntaxKind.NullKeyword,
]);
for (let kind = scanner.scan(); kind !== ts.SyntaxKind.EndOfFileToken; kind = scanner.scan()) {
if (kind === ts.SyntaxKind.CloseBraceToken && stack[stack.length - 1] === "tpl") {
kind = scanner.reScanTemplateToken(false);
if (kind === ts.SyntaxKind.TemplateTail) stack.pop();
} else if (kind === ts.SyntaxKind.CloseBraceToken) {
stack.pop();
} else if (kind === ts.SyntaxKind.OpenBraceToken) {
stack.push("brace");
} else if (kind === ts.SyntaxKind.TemplateHead) {
stack.push("tpl");
} else if (
(kind === ts.SyntaxKind.SlashToken || kind === ts.SyntaxKind.SlashEqualsToken) &&
!exprEnd.has(prevKind)
) {
kind = scanner.reScanSlashToken();
}
tokens.push(scanner.getTokenText());
prevKind = kind;
}
// prettier adds/removes trailing commas when it re-wraps after the dedent
return tokens.filter((t, i) => !(t === "," && /^[)\]}]$/.test(tokens[i + 1] ?? "")));
}
/** Myers O(ND) diff over token arrays. Returns edit hunks {aStart,aEnd,bStart,bEnd}. */
export function myersHunks(a, b, maxD = 20000) {
const n = a.length;
const m = b.length;
const max = Math.min(n + m, maxD);
const trace = [];
let v = new Int32Array(2 * max + 2);
const off = max;
let found = -1;
for (let d = 0; d <= max && found < 0; d++) {
trace.push(v.slice());
for (let k = -d; k <= d; k += 2) {
let x =
k === -d || (k !== d && v[off + k - 1] < v[off + k + 1])
? v[off + k + 1]
: v[off + k - 1] + 1;
let y = x - k;
while (x < n && y < m && a[x] === b[y]) {
x++;
y++;
}
v[off + k] = x;
if (x >= n && y >= m) {
found = d;
break;
}
}
}
if (found < 0) throw new Error(`token diff exceeds ${maxD} edits - regions are not comparable`);
// backtrack
const edits = []; // {type: "del"|"ins", ai, bi}
let x = n;
let y = m;
for (let d = found; d > 0; d--) {
const vv = trace[d];
const k = x - y;
const prevK = k === -d || (k !== d && vv[off + k - 1] < vv[off + k + 1]) ? k + 1 : k - 1;
const prevX = vv[off + prevK];
const prevY = prevX - prevK;
while (x > prevX && y > prevY) {
x--;
y--;
}
if (x === prevX) edits.push({ type: "ins", ai: x, bi: y - 1 });
else edits.push({ type: "del", ai: x - 1, bi: y });
x = prevX;
y = prevY;
}
edits.reverse();
// group into hunks (merge edits separated by < 4 matching tokens)
const hunks = [];
for (const e of edits) {
const last = hunks[hunks.length - 1];
const aPos = e.ai;
const bPos = e.bi;
if (last && aPos - last.aEnd < 4 && bPos - last.bEnd < 4 + (aPos - last.aEnd)) {
// extend
if (e.type === "del") last.aEnd = Math.max(last.aEnd, e.ai + 1);
else last.bEnd = Math.max(last.bEnd, e.bi + 1);
if (e.type === "del") last.bEnd = Math.max(last.bEnd, e.bi);
else last.aEnd = Math.max(last.aEnd, e.ai);
} else {
hunks.push({
aStart: e.ai,
aEnd: e.type === "del" ? e.ai + 1 : e.ai,
bStart: e.bi,
bEnd: e.type === "ins" ? e.bi + 1 : e.bi,
});
}
}
return hunks;
}
// Regions. `base` slices the pre-split chatCore.ts, `leaf` slices the leaf file. Patterns run
// against NORMALIZED lines (trimmed, comments and blanks dropped), so indentation is irrelevant.
const LEAF_AFTER_DEPS = /^\} = deps;$/;
export const REGIONS = [
{
name: "executeProviderRequest",
file: "open-sse/handlers/chatCore/executeProviderRequest.ts",
carryKeys: [],
allowRemoved: [],
base: {
startPattern: /^const execute = async \(\) => \{$/,
endPattern: /^return execute\(\);$/,
inclusiveEnd: true,
},
leaf: {
after: LEAF_AFTER_DEPS,
startPattern: /^const execute = async \(\) => \{$/,
endPattern: /^return execute\(\);$/,
inclusiveEnd: true,
},
},
{
name: "streamingResponse",
file: "open-sse/handlers/chatCore/streamingResponse.ts",
carryKeys: [
"translatedBody",
"currentModel",
"effectiveServiceTier",
"finalBody",
"pipelineRecovered",
"providerHeaders",
"providerResponse",
"providerUrl",
],
allowRemoved: ["; } } }"], // closers of `if (stream) {` / handleChatCoreInner left in the barrel
base: {
after: /^let pipelineRecovered = false;$/,
startPattern: /^try \{$/,
endPattern: /^if \(!stream\) \{$/,
},
leaf: {
after: LEAF_AFTER_DEPS,
startPattern: /^try \{$/,
endPattern: /^return \{$/,
endMode: "last",
},
},
{
name: "nonStreamingResponse",
file: "open-sse/handlers/chatCore/nonStreamingResponse.ts",
carryKeys: [
"translatedBody",
"currentModel",
"finalBody",
"providerResponse",
"providerHeaders",
"effectiveServiceTier",
"claudePromptCacheLogMeta",
"reasoningReplayHistory",
"pipelineRecovered",
],
allowRemoved: ["if ( ! stream ) {", "}"], // the `if (!stream)` opener / its closer stay in the barrel
base: {
startPattern: /^if \(!stream\) \{$/,
endPattern: /^providerResponse = await maybeConvertJsonBodyToSse\(/,
},
leaf: {
after: LEAF_AFTER_DEPS,
startPattern: /^try \{$/,
endPattern: /^\}$/,
endMode: "last",
},
},
{
name: "streamingTail",
file: "open-sse/handlers/chatCore/streamingTail.ts",
carryKeys: [
"onPipelineStreamError",
"onClientDisconnectFinalize",
"turnExecutionHandedOffToStream",
],
allowRemoved: [
// handleChatCoreInner's finally (turn-execution release) stays in the barrel
"; } finally { if ( ! turnExecutionHandedOffToStream ) { releaseTurnExecution ( ) ; } } }",
],
base: {
startPattern: /^providerResponse = await maybeConvertJsonBodyToSse\(/,
endPattern: /^(export )?function isTokenExpiringSoon/,
},
leaf: {
after: LEAF_AFTER_DEPS,
startPattern: /^providerResponse = await maybeConvertJsonBodyToSse\(/,
endPattern: /^\}$/,
endMode: "last",
},
},
];
const SYNC_CALL = "syncExecuteTranslatedBody ( translatedBody ) ;";
const WRAP_OPEN = new Set(["{ response :", "response : {", "result : {"]);
/** Returns a short reason when the hunk is a documented lift seam, else null. */
export function classifyHunk(removed, added, region) {
const carry = `carry : { ${region.carryKeys.join(" , ")} }`;
if (removed === "" && added === SYNC_CALL) return "re-sync of the live translatedBody closure";
if (removed === "" && WRAP_OPEN.has(added))
return "return value wrapped as { response|result, carry }";
const closeRe = new RegExp(`^, ${carry.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")} \\}( ;( \\})*)?$`);
if ((removed === "" || region.allowRemoved.includes(removed)) && closeRe.test(added)) {
return "carry object closing the wrapped return";
}
const wrapValue = removed.match(/^[\w$]+$/);
if (wrapValue && added === `{ response : ${removed} , ${carry} }`) {
return "returned identifier wrapped with the carry object";
}
if (region.allowRemoved.includes(removed) && added === "")
return "region boundary left in handleChatCore";
return null;
}
function readBase() {
try {
return execFileSync("git", ["show", `${BASE_REF}:${BASE_FILE}`], {
cwd: ROOT,
encoding: "utf8",
maxBuffer: 64 * 1024 * 1024,
});
} catch (err) {
console.error(
`[diff-chatcore-leaves] cannot read ${BASE_FILE} from ${BASE_REF}: ${err.message}`
);
process.exit(2);
}
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
main();
}
function main() {
console.log(`[diff-chatcore-leaves] base ref: ${BASE_REF}`);
const baseLines = normalize(readBase());
let total = 0;
for (const region of REGIONS) {
const leafLines = normalize(fs.readFileSync(path.join(ROOT, region.file), "utf8"));
let bt;
let lt;
try {
bt = tokenize(sliceRegion(baseLines, region.base).join("\n"));
lt = tokenize(sliceRegion(leafLines, region.leaf).join("\n"));
} catch (err) {
console.error(`[diff-chatcore-leaves] ${region.name}: cannot extract region: ${err.message}`);
process.exit(2);
}
const hunks = myersHunks(bt, lt);
let mechanical = 0;
const bad = [];
for (const h of hunks) {
const removed = bt.slice(h.aStart, h.aEnd).join(" ");
const added = lt.slice(h.bStart, h.bEnd).join(" ");
const reason = classifyHunk(removed, added, region);
if (reason) {
mechanical++;
if (VERBOSE) console.log(` ok ${reason}: -[${removed}] +[${added.slice(0, 120)}]`);
} else {
bad.push({ h, removed, added });
}
}
total += bad.length;
console.log(
`${bad.length === 0 ? "OK " : "FAIL"} ${region.name}: base ${bt.length} tokens, leaf ${lt.length} tokens, ` +
`${mechanical} mechanical hunk(s), ${bad.length} NON-MECHANICAL`
);
for (const { h, removed, added } of bad) {
console.log(` @@ after: ${bt.slice(Math.max(0, h.aStart - 8), h.aStart).join(" ")}`);
if (removed) console.log(` - ${removed}`);
if (added) console.log(` + ${added}`);
}
}
console.log(`\n[diff-chatcore-leaves] non-mechanical hunks: ${total}`);
if (total === 0) console.log("[diff-chatcore-leaves] all four leaves are mechanical lifts");
process.exit(total === 0 ? 0 : 1);
}

View file

@ -319,7 +319,7 @@ if [ "$FAST" = "1" ]; then
| grep -E '\.test\.(ts|mjs)$' || true)
# Subdirs test:unit actually runs (package.json allowlist) — anything else under
# tests/unit/<sub>/ belongs to another runner (e.g. autoCombo -> vitest).
UNIT_SUBDIRS=",api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage,"
UNIT_SUBDIRS=",api,auth,authz,build,chatcore,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage,"
MAIN=()
DASH=()
SERIAL=()

View file

@ -82,6 +82,8 @@ describe("Pipeline Wiring — instrumentation-node.ts", () => {
describe("Pipeline Wiring — sse chat handler", () => {
const src = readProjectFile("src/sse/handlers/chat.ts");
const coreSrc = readProjectFile("open-sse/handlers/chatCore.ts");
// recordCost moved with the streaming tail when handleChatCore was split into leaves.
const streamingTailSrc = readProjectFile("open-sse/handlers/chatCore/streamingTail.ts");
it("should import and use guardrail pre-call validation", () => {
assert.ok(src, "src/sse/handlers/chat.ts should exist");
@ -107,8 +109,9 @@ describe("Pipeline Wiring — sse chat handler", () => {
it("should keep cost tracking integration in the chat pipeline", () => {
assert.ok(coreSrc, "open-sse/handlers/chatCore.ts should exist");
assert.ok(streamingTailSrc, "open-sse/handlers/chatCore/streamingTail.ts should exist");
assert.match(coreSrc, /calculateCost/);
assert.match(coreSrc, /recordCost/);
assert.match(streamingTailSrc, /recordCost/);
});
it("should not track backup artifacts in the active src/sse shim", () => {

View file

@ -123,29 +123,34 @@ test("loadPlugin no longer spawns the plugin host with stdout/stderr fully ignor
// contract test for runPluginOnResponseHook itself
// (tests/unit/chatcore-plugin-onresponse.test.ts).
test("chatCore.ts calls runPluginOnResponseHook from both the non-streaming and streaming success paths", async () => {
const source = await readFile(
join(import.meta.dirname, "../../open-sse/handlers/chatCore.ts"),
// After the chatCore decomposition the two success-path call sites live in the
// leg leaves: non-streaming in nonStreamingResponse.ts, streaming in streamingTail.ts.
const nonStreamingSource = await readFile(
join(import.meta.dirname, "../../open-sse/handlers/chatCore/nonStreamingResponse.ts"),
"utf-8"
);
const streamingSource = await readFile(
join(import.meta.dirname, "../../open-sse/handlers/chatCore/streamingTail.ts"),
"utf-8"
);
const nonStreamingReturnIndex = source.indexOf("maybeWrapForcedNonStreamingResponsesJson({");
const hookCallNeedle = "await runPluginOnResponseHook({";
const hookCallIndex = source.indexOf(hookCallNeedle);
const secondHookCallIndex = source.indexOf(hookCallNeedle, hookCallIndex + 1);
const nonStreamingReturnIndex = nonStreamingSource.indexOf("maybeWrapForcedNonStreamingResponsesJson({");
const hookCallIndex = nonStreamingSource.indexOf(hookCallNeedle);
const streamingHookCallIndex = streamingSource.indexOf(hookCallNeedle);
assert.notEqual(hookCallIndex, -1, "expected at least one runPluginOnResponseHook call site");
assert.notEqual(hookCallIndex, -1, "expected a runPluginOnResponseHook call site in the non-streaming leg");
assert.notEqual(
secondHookCallIndex,
streamingHookCallIndex,
-1,
"expected TWO runPluginOnResponseHook call sites — one per success branch " +
"(non-streaming JSON return and streaming SSE return)"
"expected a runPluginOnResponseHook call site in the streaming leg — one per success branch"
);
assert.ok(
source.indexOf("response: { status: 200, data: translatedResponse }") !== -1,
nonStreamingSource.indexOf("response: { status: 200, data: translatedResponse }") !== -1,
"non-streaming branch must pass translatedResponse as plugin response data (#8711)"
);
assert.ok(
source.indexOf("response: { status: 200, streamed: true }") !== -1,
streamingSource.indexOf("response: { status: 200, streamed: true }") !== -1,
"streaming branch must pass streamed:true without materializing SSE body (#8711)"
);
assert.ok(

View file

@ -164,13 +164,17 @@ test("direct Antigravity has one downstream model-lock owner and clamps body pro
path.resolve(import.meta.dirname, "../../open-sse/handlers/chatCore.ts"),
"utf8"
);
const eprSource = fs.readFileSync(
path.resolve(import.meta.dirname, "../../open-sse/handlers/chatCore/executeProviderRequest.ts"),
"utf8"
);
assert.match(
chatCoreSource,
/accountSemaphoreKey && !deferAntigravityQuotaStateToCaller/,
"chatCore must not apply a prose-derived Antigravity semaphore TTL"
);
assert.match(
chatCoreSource,
eprSource,
/Dropped generic quota cache after 429/,
"non-Codex 429 must leave a QUOTA debug breadcrumb"
);

View file

@ -25,8 +25,10 @@ test("FETCH_BODY_TIMEOUT_MS defaults to FETCH_TIMEOUT_MS when no env override",
// ── 2. BodyTimeoutError classification in chatCore ──────────────────────
test("chatCore error classification maps BodyTimeoutError to 504 GATEWAY_TIMEOUT", () => {
// Read the source to verify the error classification logic includes BodyTimeoutError
const content = fs.readFileSync("open-sse/handlers/chatCore.ts", "utf8");
// Read the leaf to verify the error classification logic includes BodyTimeoutError
// (the streaming leg's catch block lives in chatCore/streamingResponse.ts since
// the chatCore decomposition; the barrel no longer holds the classification).
const content = fs.readFileSync("open-sse/handlers/chatCore/streamingResponse.ts", "utf8");
// The error classification block should include BodyTimeoutError alongside TimeoutError.
// Match whatever identifier carries the error (#13910 renamed it to `errorMetadata`);
@ -40,14 +42,19 @@ test("chatCore error classification maps BodyTimeoutError to 504 GATEWAY_TIMEOUT
});
test("chatCore catch block decrements pending requests for all error types", () => {
const content = fs.readFileSync("open-sse/handlers/chatCore.ts", "utf8");
// The catch block should call trackPendingRequest with false before error classification
const catchBlockPattern = /catch\s*\(error\)\s*\{[^}]*trackPendingRequest\([^)]*,\s*false\b/;
assert.ok(
catchBlockPattern.test(content),
"chatCore catch block should decrement pending requests"
);
// After the chatCore decomposition the per-leg catches live in the leaves;
// assert both legs decrement pending requests at the top of their catch.
for (const leaf of [
"open-sse/handlers/chatCore/nonStreamingResponse.ts",
"open-sse/handlers/chatCore/streamingResponse.ts",
]) {
const content = fs.readFileSync(leaf, "utf8");
const catchBlockPattern = /catch\s*\(error\)\s*\{[^}]*trackPendingRequest\([^)]*,\s*false\b/;
assert.ok(
catchBlockPattern.test(content),
`${leaf} catch block should decrement pending requests`
);
}
});
test("withBodyTimeout error name is BodyTimeoutError", async () => {

View file

@ -2,8 +2,10 @@ import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { test } from "node:test";
// After the chatCore decomposition the wire-send path lives in
// chatCore/executeProviderRequest.ts; the barrel only wires it up.
const source = readFileSync(
new URL("../../open-sse/handlers/chatCore.ts", import.meta.url),
new URL("../../open-sse/handlers/chatCore/executeProviderRequest.ts", import.meta.url),
"utf8"
);
@ -12,6 +14,17 @@ const pipeline = readFileSync(
"utf8"
);
const legs = [
readFileSync(
new URL("../../open-sse/handlers/chatCore/nonStreamingResponse.ts", import.meta.url),
"utf8"
),
readFileSync(
new URL("../../open-sse/handlers/chatCore/streamingResponse.ts", import.meta.url),
"utf8"
),
];
test("chatCore acquires cumulative gates immediately before withRateLimit", () => {
const acquire = source.indexOf("await acquireConcurrencyGates(");
const rateLimit = source.indexOf("await withRateLimit(", acquire);
@ -35,7 +48,7 @@ test("chatCore acquires cumulative gates immediately before withRateLimit", () =
// single index check covered it; the loop is now split across two files, so the
// guard checks both halves of the same invariant.
test("each rotated account attempt acquires and releases a fresh composite slot", () => {
const sendFn = source.indexOf("const executeProviderRequest = async (");
const sendFn = source.indexOf("export async function executeProviderRequest(");
const attemptLoop = source.indexOf("while (attempts < maxAttempts)", sendFn);
const acquire = source.indexOf("await acquireConcurrencyGates(", attemptLoop);
const release = source.indexOf("releaseAccountSemaphore();", acquire);
@ -52,10 +65,12 @@ test("each rotated account attempt acquires and releases a fresh composite slot"
"a throwing attempt must release the composite slot"
);
const sendWirings =
source.match(
/sendProviderAttempt: \(modelToCall, allowDedup\) =>\s*executeProviderRequest\(modelToCall, allowDedup\)/g
) ?? [];
const sendWirings = legs.flatMap(
(leg) =>
leg.match(
/sendProviderAttempt: \(modelToCall, allowDedup\) =>\s*executeProviderRequest\(modelToCall, allowDedup\)/g
) ?? []
);
assert.equal(sendWirings.length, 2, "both legs send every pipeline attempt through the gate");
const rotationLoop = pipeline.search(/while \(\s*attempts < maxAttempts\b/);

View file

@ -156,13 +156,15 @@ test("getUpstreamErrorIdentifier returns a non-empty string code or undefined",
test("non-streaming runNonStreamingProviderLeg is inside a try that maps semaphore errors", async () => {
const fs = await import("node:fs");
const src = fs.readFileSync("open-sse/handlers/chatCore.ts", "utf8");
// After the decomposition the non-streaming branch lives in the leaf; its
// function-level try/catch is what maps semaphore errors.
const src = fs.readFileSync("open-sse/handlers/chatCore/nonStreamingResponse.ts", "utf8");
// `let`, not `const`, since 6077b9dd (#12867) made the finalization step reassign
// legResult. The guard is about the try/catch that wraps the call, not the keyword.
const idx = src.search(/(?:const|let) legResult = await runNonStreamingProviderLeg/);
assert.ok(idx >= 0, "non-streaming branch must exist");
const start = src.lastIndexOf("if (!stream)", idx);
const end = src.indexOf("// Streaming response", idx);
const start = src.lastIndexOf("export async function runNonStreamingResponse(", idx);
const end = src.indexOf("} catch (error)", idx);
assert.ok(start >= 0 && end > start, "non-stream block bounds");
const block = src.slice(start, end);
assert.match(
@ -170,8 +172,9 @@ test("non-streaming runNonStreamingProviderLeg is inside a try that maps semapho
/try\s*\{[\s\S]*runNonStreamingProviderLeg/,
"non-stream leg must sit in a try so SEMAPHORE_TIMEOUT cannot escape handleChatCore"
);
const catchBlock = src.slice(end);
assert.match(
block,
catchBlock,
/isSemaphoreCapacityError/,
"same catch that maps stream semaphore errors must cover the non-stream leg"
);

View file

@ -0,0 +1,71 @@
import test from "node:test";
import assert from "node:assert/strict";
import {
classifyHunk,
myersHunks,
normalize,
sliceRegion,
tokenize,
// @ts-expect-error - plain .mjs dev script without type declarations
} from "../../../scripts/dev/diff-chatcore-leaves.mjs";
const region = {
carryKeys: ["a", "b"],
allowRemoved: [],
};
test("tokenize ignores comments, whitespace and prettier re-wrap commas", () => {
const wrapped = tokenize("foo(\n a, // note\n b,\n);");
const flat = tokenize("foo(a, b);");
assert.deepEqual(wrapped, flat);
});
test("normalize drops blank, line-comment and block-comment lines", () => {
assert.deepEqual(normalize(" a = 1;\n\n// c\n/* x\n y */\n b;"), ["a = 1;", "b;"]);
});
test("sliceRegion honours the anchor, start and end patterns", () => {
const lines = ["x", "start", "y", "end", "start", "z", "end"];
assert.deepEqual(sliceRegion(lines, { startPattern: /^start$/, endPattern: /^end$/ }), [
"start",
"y",
]);
assert.deepEqual(
sliceRegion(lines, {
after: /^end$/,
startPattern: /^start$/,
endPattern: /^end$/,
inclusiveEnd: true,
}),
["start", "z", "end"]
);
});
test("identical token streams produce no hunks; a changed literal produces one", () => {
const a = tokenize('return fail(499, "Request aborted");');
assert.equal(myersHunks(a, [...a]).length, 0);
const b = tokenize('return fail(499, "Request aborted!");');
const hunks = myersHunks(a, b);
assert.equal(hunks.length, 1);
assert.equal(
classifyHunk(
a.slice(hunks[0].aStart, hunks[0].aEnd).join(" "),
b.slice(hunks[0].bStart, hunks[0].bEnd).join(" "),
region
),
null,
"a rewritten literal is NOT a mechanical seam"
);
});
test("classifyHunk accepts only the documented lift seams", () => {
assert.ok(classifyHunk("", "syncExecuteTranslatedBody ( translatedBody ) ;", region));
assert.ok(classifyHunk("", "{ response :", region));
assert.ok(classifyHunk("", ", carry : { a , b } }", region));
assert.ok(classifyHunk("result", "{ response : result , carry : { a , b } }", region));
// wrong carry keys / unknown additions are rejected
assert.equal(classifyHunk("", ", carry : { a , c } }", region), null);
assert.equal(classifyHunk("result", "{ response : other , carry : { a , b } }", region), null);
assert.equal(classifyHunk("", "doSomethingNew ( ) ;", region), null);
});

View file

@ -0,0 +1,101 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
const BARREL = path.join(ROOT, "open-sse/handlers/chatCore.ts");
const LEAF = path.join(ROOT, "open-sse/handlers/chatCore/executeProviderRequest.ts");
function read(file: string): string {
return fs.readFileSync(file, "utf8");
}
test("executeProviderRequest lives in its own leaf", () => {
assert.equal(fs.existsSync(LEAF), true);
const leaf = read(LEAF);
assert.match(leaf, /export async function executeProviderRequest\(/);
assert.equal(
/const executeProviderRequest = async/.test(read(BARREL)),
false
);
});
test("the leaf keeps the tip send shape", () => {
const leaf = read(LEAF);
assert.match(leaf, /prepareUpstreamBody\(/);
assert.match(leaf, /injectSystemPromptPostTranslation\(/);
assert.equal(
leaf.includes("isClaudePassthrough"),
false,
"tip executor.execute does not take isClaudePassthrough"
);
});
test("handleChatCore calls the leaf and still records the cost ledger", () => {
const barrel = read(BARREL);
assert.match(barrel, /executeProviderRequestLeaf\(executeProviderRequestDeps/);
const nonStreaming = read(path.join(ROOT, "open-sse/handlers/chatCore/nonStreamingResponse.ts"));
assert.match(nonStreaming, /recordChatCallCost\(/);
const tail = read(path.join(ROOT, "open-sse/handlers/chatCore/streamingTail.ts"));
assert.match(tail, /buildStreamLedgerDetails\(/);
assert.match(nonStreaming, /buildCostCtx\(/);
});
test("syncExecuteTranslatedBody seam keeps executeProviderRequestDeps synchronized across rebindings", () => {
const barrel = read(BARREL);
assert.match(
barrel,
/const syncExecuteTranslatedBody = \(next: unknown\) => \{[\s\S]*?executeProviderRequestDeps\.translatedBody = next as Record<string, unknown>;/,
"chatCore must define syncExecuteTranslatedBody and update executeProviderRequestDeps.translatedBody"
);
// Functional simulation of the closure / deps reference seam:
const executeProviderRequestDeps: Record<string, unknown> = {
translatedBody: { model: "base-model", messages: [] },
};
const syncExecuteTranslatedBody = (next: unknown) => {
executeProviderRequestDeps.translatedBody = next as Record<string, unknown>;
};
const simulateDispatch = (deps: typeof executeProviderRequestDeps) => {
return deps.translatedBody;
};
assert.deepEqual(simulateDispatch(executeProviderRequestDeps), {
model: "base-model",
messages: [],
});
// Rebind via syncExecuteTranslatedBody (e.g. tool follow-up or pipeline wire update):
const reboundBody = { model: "base-model", messages: [{ role: "assistant", content: "followup" }] };
syncExecuteTranslatedBody(reboundBody);
assert.strictEqual(
simulateDispatch(executeProviderRequestDeps),
reboundBody,
"subsequent dispatches must observe the rebound translatedBody reference"
);
// Verify that the seam is wired into both streaming and non-streaming response leaves:
assert.match(
barrel,
/runNonStreamingResponse\(\{[\s\S]*?syncExecuteTranslatedBody,/,
"runNonStreamingResponse must receive syncExecuteTranslatedBody"
);
assert.match(
barrel,
/runStreamingResponse\(\{[\s\S]*?syncExecuteTranslatedBody,/,
"runStreamingResponse must receive syncExecuteTranslatedBody"
);
// Invariant: every reassignment of translatedBody in the leaves must immediately sync back
const nonStreaming = read(path.join(ROOT, "open-sse/handlers/chatCore/nonStreamingResponse.ts"));
const streaming = read(path.join(ROOT, "open-sse/handlers/chatCore/streamingResponse.ts"));
const nonStreamingSyncCount = (nonStreaming.match(/syncExecuteTranslatedBody\(translatedBody\)/g) ?? []).length;
assert.ok(nonStreamingSyncCount >= 5, "nonStreamingResponse must sync on every translatedBody rebinding");
const streamingSyncCount = (streaming.match(/syncExecuteTranslatedBody\(translatedBody\)/g) ?? []).length;
assert.ok(streamingSyncCount >= 2, "streamingResponse must sync on every translatedBody rebinding");
});

View file

@ -0,0 +1,86 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import ts from "typescript";
// The streaming/non-streaming/tail leaves take a loosely typed deps bag
// (Record<string, any>), so the compiler cannot notice a key the leaf reads
// but chatCore.ts never passes — the leaf then dies at runtime with
// "x is not a function" (the streaming direct-refresh retry lost
// getExecutorClientHeaders this way). Assert the contract structurally.
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
const BARREL = path.join(ROOT, "open-sse/handlers/chatCore.ts");
const LEAVES: Record<string, string> = {
runStreamingResponse: "open-sse/handlers/chatCore/streamingResponse.ts",
runNonStreamingResponse: "open-sse/handlers/chatCore/nonStreamingResponse.ts",
runStreamingTail: "open-sse/handlers/chatCore/streamingTail.ts",
};
function parse(file: string): ts.SourceFile {
return ts.createSourceFile(file, fs.readFileSync(file, "utf8"), ts.ScriptTarget.Latest, true);
}
function passedKeys(sf: ts.SourceFile, fn: string): Set<string> {
const keys = new Set<string>();
const walk = (node: ts.Node) => {
if (
ts.isCallExpression(node) &&
ts.isIdentifier(node.expression) &&
node.expression.text === fn
) {
const arg = node.arguments[0];
if (arg && ts.isObjectLiteralExpression(arg)) {
for (const prop of arg.properties) {
const name = prop.name;
if (name && (ts.isIdentifier(name) || ts.isStringLiteral(name))) keys.add(name.text);
}
}
}
ts.forEachChild(node, walk);
};
walk(sf);
return keys;
}
function readKeys(sf: ts.SourceFile): Set<string> {
const keys = new Set<string>();
const walk = (node: ts.Node) => {
if (
ts.isVariableDeclaration(node) &&
ts.isObjectBindingPattern(node.name) &&
node.initializer &&
ts.isIdentifier(node.initializer) &&
node.initializer.text === "deps"
) {
for (const el of node.name.elements) {
keys.add(((el.propertyName ?? el.name) as ts.Identifier).text);
}
}
if (
ts.isPropertyAccessExpression(node) &&
ts.isIdentifier(node.expression) &&
node.expression.text === "deps"
) {
keys.add(node.name.text);
}
ts.forEachChild(node, walk);
};
walk(sf);
return keys;
}
const barrel = parse(BARREL);
for (const [fn, leafPath] of Object.entries(LEAVES)) {
test(`${fn}: chatCore.ts passes every deps key the leaf reads`, () => {
const read = readKeys(parse(path.join(ROOT, leafPath)));
const given = passedKeys(barrel, fn);
assert.ok(read.size > 0, `no deps keys found in ${leafPath}`);
assert.ok(given.size > 0, `no ${fn}({...}) call found in chatCore.ts`);
const missing = [...read].filter((key) => !given.has(key));
assert.deepEqual(missing, [], `${fn} reads deps keys chatCore.ts never passes`);
});
}

View file

@ -0,0 +1,40 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import test from "node:test";
const ROOT = path.resolve(import.meta.dirname, "../../..");
const read = (p) => readFileSync(path.join(ROOT, p), "utf8");
const LEAF = "open-sse/handlers/chatCore/nonStreamingResponse.ts";
const BARREL = "open-sse/handlers/chatCore.ts";
test("the non-streaming path lives in its own leaf", () => {
const leaf = read(LEAF);
assert.match(leaf, /export async function runNonStreamingResponse\(/);
assert.doesNotMatch(read(BARREL), /const runNonStreamingPipeline = async/);
});
test("the barrel calls the leaf and writes the carried state back", () => {
const barrel = read(BARREL);
assert.match(barrel, /runNonStreamingResponse\(/);
for (const name of ["translatedBody", "currentModel", "finalBody", "providerResponse", "pipelineRecovered"]) {
assert.match(barrel, new RegExp("carry\\." + name + "\\b"));
}
});
test("the cost ledger stays recorded on the non-streaming path", () => {
const leaf = read(LEAF);
assert.match(leaf, /recordChatCallCost\(/);
assert.match(leaf, /buildCostCtx\(/);
});
test("the barrel imports the leaf (no-undef is disabled for TS, so assert it)", () => {
const barrel = read(BARREL);
assert.match(barrel, /import \{\s*runNonStreamingResponse,?\s*\} from "\.\/chatCore\/nonStreamingResponse\.ts";/);
});
test("every leaf exit carries state (the leg-error exit must not return bare)", () => {
const leaf = read(LEAF);
const bareReturns = leaf.match(/^\s*return err;$/gm);
assert.equal(bareReturns, null, "a bare `return err;` leaves the barrel reading .carry of undefined");
});

View file

@ -0,0 +1,42 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import test from "node:test";
const ROOT = path.resolve(import.meta.dirname, "../../..");
const read = (p) => readFileSync(path.join(ROOT, p), "utf8");
const LEAF = "open-sse/handlers/chatCore/streamingResponse.ts";
const BARREL = "open-sse/handlers/chatCore.ts";
test("the streaming path lives in its own leaf", () => {
const leaf = read(LEAF);
assert.match(leaf, /export async function runStreamingResponse\(/);
assert.doesNotMatch(read(BARREL), /const pipelineOutcome = await runProviderExecutionPipeline/);
});
test("the barrel calls the leaf and writes the carried state back", () => {
const barrel = read(BARREL);
assert.match(barrel, /runStreamingResponse\(/);
for (const name of ["translatedBody", "currentModel", "providerUrl", "pipelineRecovered"]) {
assert.match(barrel, new RegExp("carry\\." + name + "\\b"));
}
});
test("the barrel imports the leaf (no-undef is disabled for TS, so assert it)", () => {
const barrel = read(BARREL);
assert.match(barrel, /import \{\s*runStreamingResponse,?\s*\} from "\.\/chatCore\/streamingResponse\.ts";/);
});
test("message stays block-local (declared inside providerFailure, never carried)", () => {
const leaf = read(LEAF);
const barrel = read(BARREL);
assert.doesNotMatch(leaf, /finalBody, message, pipelineRecovered/);
assert.doesNotMatch(barrel, /message = streamingOutcome\.carry\.message/);
});
test("streaming cost accounting stays out of the response leaf", () => {
// It lives in the streaming tail (see streaming-tail.test.ts); the response
// leaf only executes the provider call and must not touch the ledger.
const leaf = read(LEAF);
assert.doesNotMatch(leaf, /recordStreamingCost\(/);
});

View file

@ -0,0 +1,43 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import test from "node:test";
const ROOT = path.resolve(import.meta.dirname, "../../..");
const read = (p) => readFileSync(path.join(ROOT, p), "utf8");
const LEAF = "open-sse/handlers/chatCore/streamingTail.ts";
const BARREL = "open-sse/handlers/chatCore.ts";
test("the streaming tail lives in its own leaf", () => {
const leaf = read(LEAF);
assert.match(leaf, /export async function runStreamingTail\(/);
assert.doesNotMatch(read(BARREL), /const onStreamComplete = \(\{/);
});
test("the barrel calls the leaf and writes the carried state back", () => {
const barrel = read(BARREL);
assert.match(barrel, /runStreamingTail\(/);
for (const name of [
"onPipelineStreamError",
"onClientDisconnectFinalize",
"turnExecutionHandedOffToStream",
]) {
assert.match(barrel, new RegExp("carry\\." + name + "\\b"));
}
});
test("the barrel imports the leaf (no-undef is disabled for TS, so assert it)", () => {
const barrel = read(BARREL);
assert.match(
barrel,
/import \{\s*runStreamingTail,?\s*\} from "\.\/chatCore\/streamingTail\.ts";/
);
});
test("streaming cost accounting lives in the tail leaf, not the barrel", () => {
const leaf = read(LEAF);
assert.match(leaf, /recordStreamingCost\(/);
assert.match(leaf, /buildStreamLedgerDetails\(/);
const barrel = read(BARREL);
assert.doesNotMatch(barrel, /recordStreamingCost\(/);
});

View file

@ -398,10 +398,15 @@ test("stream request finalization never warns with a raw error object", () => {
test("chatCore provider-failure writes use the projected persistent message", () => {
const source = fs.readFileSync(path.join(REPO_ROOT, "open-sse/handlers/chatCore.ts"), "utf8");
const failureStart = source.indexOf("providerFailure: if (!providerResponse.ok)");
const failureEnd = source.indexOf("// Non-streaming response", failureStart);
assert.ok(failureStart >= 0 && failureEnd > failureStart, "providerFailure block must exist");
const failureBlock = source.slice(failureStart, failureEnd);
// The providerFailure block moved into the streaming leaf with the handleChatCore split; the
// classification helper (applyProviderFailureClassification) stayed in the barrel.
const streamingLeaf = fs.readFileSync(
path.join(REPO_ROOT, "open-sse/handlers/chatCore/streamingResponse.ts"),
"utf8"
);
const failureStart = streamingLeaf.indexOf("providerFailure: if (!providerResponse.ok)");
assert.ok(failureStart >= 0, "providerFailure block must exist in streamingResponse.ts");
const failureBlock = streamingLeaf.slice(failureStart);
const classifierStart = source.indexOf("const applyProviderFailureClassification = async");
const classifierEnd = source.indexOf("\n };\n", classifierStart);
assert.ok(

View file

@ -24,9 +24,11 @@ const EXPECTED: Record<InventoryKind, Record<string, number>> = {
// executeProviderRequest(), whose assertManagedLeaseFence(attemptConnectionId) rejects a
// connection other than the leased one — so it is fenced centrally (class A).
// #14914 moved that loop (and its credential rollback) into
// chatCore/emptyTurnRetryLoop.ts; chatCore.ts now passes `getProviderCredentials` in
// as a dependency (a reference, not a call), so the site is inventoried at its new
// home — still dispatched through executeProviderRequest(), still class A.
// chatCore/emptyTurnRetryLoop.ts; the response-path split (chatCore.ts split into
// response-path leaves) then moved the call site into streamingTail.ts, which now
// passes `getProviderCredentials` in as a dependency (a reference, not a call) to
// emptyTurnRetryLoop.ts — still dispatched through executeProviderRequest(), still
// fenced centrally (class A).
"open-sse/handlers/chatCore/emptyTurnRetryLoop.ts": 1,
"open-sse/handlers/chatCore/providerExecutionPipeline.ts": 2,
"open-sse/services/imageCombo.ts": 1,
@ -80,7 +82,11 @@ const EXPECTED: Record<InventoryKind, Record<string, number>> = {
"src/sse/services/imageCredentialRetry.ts": 1,
},
executor: {
"open-sse/handlers/chatCore.ts": 3,
// The three executor.execute() sites that used to sit in chatCore.ts moved
// with the decomposition: two into the wire-send leaf and one into the
// streaming leg (same sites, new homes).
"open-sse/handlers/chatCore/executeProviderRequest.ts": 2,
"open-sse/handlers/chatCore/streamingResponse.ts": 1,
"open-sse/handlers/chatCore/cliproxyModelMapping.ts": 1,
"open-sse/handlers/chatCore/cliproxyapiCredentials.ts": 1,
// v3.8.51 #11754: the legacy common ChatGPT Web's synthetic
@ -97,7 +103,10 @@ const EXPECTED: Record<InventoryKind, Record<string, number>> = {
},
connection: {
"open-sse/handlers/autoComboCandidates.ts": 1,
"open-sse/handlers/chatCore.ts": 3,
// Two of the three connection re-resolution sites moved into the streaming
// leg with the decomposition (same sites, new home).
"open-sse/handlers/chatCore.ts": 1,
"open-sse/handlers/chatCore/streamingResponse.ts": 2,
"open-sse/handlers/cursorCliProxy.ts": 1,
"open-sse/services/alibabaFreeTier.ts": 1,
"open-sse/services/alibabaFreeTierQuotaFetcher.ts": 1,
@ -257,7 +266,8 @@ const CLASSIFICATION: Record<InventoryKind, Record<string, BypassClass>> = {
])
),
executor: {
"open-sse/handlers/chatCore.ts": "A",
"open-sse/handlers/chatCore/executeProviderRequest.ts": "A",
"open-sse/handlers/chatCore/streamingResponse.ts": "A",
"open-sse/handlers/chatCore/cliproxyModelMapping.ts": "A",
"open-sse/handlers/chatCore/cliproxyapiCredentials.ts": "A",
"open-sse/handlers/videoGeneration.ts": "B",
@ -271,6 +281,7 @@ const CLASSIFICATION: Record<InventoryKind, Record<string, BypassClass>> = {
[
"open-sse/handlers/autoComboCandidates.ts",
"open-sse/handlers/chatCore.ts",
"open-sse/handlers/chatCore/streamingResponse.ts",
"open-sse/services/alibabaFreeTier.ts",
"open-sse/services/alibabaFreeTierQuotaFetcher.ts",
"open-sse/services/combo/executeTargetGates.ts",
@ -380,7 +391,6 @@ test("hard-lease credential, executor, and connection-query inventory has no unc
test("managed request surfaces are fenced centrally or rejected before independent dispatch", () => {
const chat = fs.readFileSync(path.join(REPO_ROOT, "src/sse/handlers/chat.ts"), "utf8");
const core = fs.readFileSync(path.join(REPO_ROOT, "open-sse/handlers/chatCore.ts"), "utf8");
const ws = fs.readFileSync(
path.join(REPO_ROOT, "src/app/api/internal/codex-responses-ws/route.ts"),
"utf8"
@ -407,9 +417,22 @@ test("managed request surfaces are fenced centrally or rejected before independe
assert.match(chat, /parseManagedLeaseRequestContext\(request\.headers\)/);
assert.match(chat, /isManagedComboUnsupported/);
assert.match(core, /assertManagedLeaseFence\(attemptConnectionId\)/);
// The fence call sites moved into the leg leaves with the decomposition.
const eprSource = fs.readFileSync(
path.join(REPO_ROOT, "open-sse/handlers/chatCore/executeProviderRequest.ts"),
"utf8"
);
const streamingLeg = fs.readFileSync(
path.join(REPO_ROOT, "open-sse/handlers/chatCore/streamingResponse.ts"),
"utf8"
);
const nonStreamingLeg = fs.readFileSync(
path.join(REPO_ROOT, "open-sse/handlers/chatCore/nonStreamingResponse.ts"),
"utf8"
);
assert.match(eprSource, /assertManagedLeaseFence\(attemptConnectionId\)/);
assert.match(
core,
streamingLeg,
/assertManagedLeaseFence\(getExecutionConnectionId\(getExecutionCredentials\(\)\)\)/
);
// #12867 (d6f315018) extracted codex 429 / antigravity 422 account rotation out
@ -423,9 +446,14 @@ test("managed request surfaces are fenced centrally or rejected before independe
path.join(REPO_ROOT, "open-sse/handlers/chatCore/providerExecutionPipeline.ts"),
"utf8"
);
const rotationPolicySites = core.match(
/allowAccountRotation: !managedLease && comboStrategy !== "context-relay"/g
);
const rotationPolicySites = [
...nonStreamingLeg.matchAll(
/allowAccountRotation: !managedLease && comboStrategy !== "context-relay"/g
),
...streamingLeg.matchAll(
/allowAccountRotation: !managedLease && comboStrategy !== "context-relay"/g
),
];
assert.equal(
rotationPolicySites?.length,
2,

View file

@ -69,12 +69,20 @@ test("chatCore stamps clientRequestedResponsesStream before forcing stream:false
);
const stamp = source.indexOf("clientRequestedResponsesStream = true");
const force = source.indexOf("(body as Record<string, unknown>).stream = false");
const wrap = source.indexOf("maybeWrapForcedNonStreamingResponsesJson({");
const leafSource = await readFile(
join(import.meta.dirname, "../../open-sse/handlers/chatCore/nonStreamingResponse.ts"),
"utf-8"
);
const wrap = leafSource.indexOf("maybeWrapForcedNonStreamingResponsesJson({");
const leafCall = source.indexOf("await runNonStreamingResponse(");
assert.ok(stamp !== -1, "must stamp the client-requested stream flag");
assert.ok(force !== -1, "must still force stream:false for the web_search fallback");
assert.ok(wrap !== -1, "must wrap the non-streaming JSON return");
assert.ok(stamp < force, "stamp must happen before stream:false");
assert.ok(wrap > force, "wrap must happen on the non-streaming return after the force");
assert.ok(
leafCall > force,
"the non-streaming leaf runs after the force, so its wrap lands after it"
);
});
// When the client speaks the Responses API, the forced non-streaming leg is

View file

@ -112,25 +112,61 @@ test("R10: chatCore wires applyStatusRestatement into the providerFailure block"
// (side-effectful DB/env wiring), so the wiring contract is asserted at the
// source level: the hook must exist, run against the parsed error, and
// reassign both statusCode and retryAfterMs BEFORE classification.
// The classification helper stays in the barrel; the providerFailure block
// moved into the streaming leg with the decomposition.
const { readFile } = await import("node:fs/promises");
const src = await readFile(
new URL("../../open-sse/handlers/chatCore.ts", import.meta.url),
"utf8"
);
assert.match(src, /applyStatusRestatement\(/, "chatCore must call applyStatusRestatement");
const legSrc = await readFile(
new URL("../../open-sse/handlers/chatCore/streamingResponse.ts", import.meta.url),
"utf8"
);
assert.match(
legSrc,
/applyStatusRestatement\(/,
"streaming leg must call applyStatusRestatement"
);
const helperIndex = src.indexOf("const applyProviderFailureClassification = async (");
const blockIndex = src.indexOf("providerFailure: if (!providerResponse.ok)");
assert.ok(helperIndex > -1 && blockIndex > helperIndex, "classification helper and block exist");
const helperEnd = src.indexOf("const streamingOutcome = await runStreamingResponse(");
assert.ok(
helperIndex > -1 && helperEnd > helperIndex,
"classification helper exists before streaming dispatch"
);
const classifyCalls = src.match(/classifyProviderError\(/g) ?? [];
assert.equal(classifyCalls.length, 1, "chatCore classifies provider errors in exactly one place");
const classifyIndex = src.indexOf("classifyProviderError(statusCode");
assert.ok(
classifyIndex > helperIndex && classifyIndex < blockIndex,
classifyIndex > helperIndex && classifyIndex < helperEnd,
"classifyProviderError must live inside applyProviderFailureClassification"
);
const block = src.slice(blockIndex);
const blockIndex = legSrc.indexOf("providerFailure: if (!providerResponse.ok)");
assert.ok(blockIndex > -1, "providerFailure block exists in streamingResponse.ts");
// Cross-file form of the original `classifyIndex < blockIndex` ordering: the classification
// helper (holding the single classifyProviderError call) must be defined before the dispatch
// that hands it to the leaf containing the providerFailure block, and the dispatch must
// actually pass it in.
const dispatchIndex = src.indexOf("await runStreamingResponse({");
assert.ok(dispatchIndex > -1, "streaming dispatch exists in chatCore.ts");
assert.ok(
classifyIndex < dispatchIndex,
"classifyProviderError must be classified-before the block: helper precedes the streaming dispatch"
);
assert.match(
src.slice(dispatchIndex, src.indexOf("});", dispatchIndex)),
/\bapplyProviderFailureClassification,/,
"streaming dispatch must pass applyProviderFailureClassification into the leaf"
);
assert.equal(
legSrc.indexOf("classifyProviderError("),
-1,
"streaming leg does not classify directly; it delegates to applyProviderFailureClassification"
);
const block = legSrc.slice(blockIndex);
const hookIndex = block.indexOf("applyStatusRestatement(");
const statusReassign = block.indexOf("statusCode = restatement.status;");
const retryReassign = block.indexOf("retryAfterMs = restatement.retryAfterMs;");