@bitkyc08/opencodex 2.12.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.md +1 -1
  2. package/gui/dist/assets/index-BHldBl6_.js +76 -0
  3. package/gui/dist/index.html +1 -1
  4. package/package.json +1 -1
  5. package/src/adapters/anthropic-output-schema.ts +137 -0
  6. package/src/adapters/anthropic.ts +15 -0
  7. package/src/adapters/google-antigravity-replay.ts +269 -5
  8. package/src/adapters/openai-chat.ts +25 -18
  9. package/src/adapters/openai-responses.ts +63 -17
  10. package/src/bridge.ts +11 -5
  11. package/src/claude/inbound.ts +24 -3
  12. package/src/cli/account-api.ts +2 -0
  13. package/src/cli/account-extended.ts +262 -1
  14. package/src/cli/account.ts +3 -1
  15. package/src/cli/dispatch.ts +532 -0
  16. package/src/cli/doctor.ts +26 -21
  17. package/src/cli/help.ts +9 -278
  18. package/src/cli/index.ts +45 -506
  19. package/src/cli/lab.ts +171 -65
  20. package/src/cli/registry.ts +384 -0
  21. package/src/cli/root.ts +86 -0
  22. package/src/cli/status.ts +9 -2
  23. package/src/cli/v2.ts +37 -2
  24. package/src/codex/account-label.ts +14 -1
  25. package/src/codex/account-store.ts +12 -1
  26. package/src/codex/admission.ts +2 -1
  27. package/src/codex/auth-api.ts +3 -2
  28. package/src/codex/catalog/metadata.ts +6 -4
  29. package/src/codex/catalog/parsing.ts +31 -7
  30. package/src/codex/catalog/provider-fetch.ts +2 -1
  31. package/src/codex/catalog/sync.ts +65 -11
  32. package/src/codex/features.ts +437 -31
  33. package/src/codex/history-job.ts +6 -11
  34. package/src/codex/history-provider.ts +10 -9
  35. package/src/codex/inject.ts +14 -1
  36. package/src/codex/native-profile-startup.ts +49 -3
  37. package/src/codex/native-residue.ts +14 -2
  38. package/src/codex/paths.ts +75 -1
  39. package/src/codex/reset-credit-recovery.ts +1044 -0
  40. package/src/codex/shim.ts +876 -16
  41. package/src/codex/sync.ts +27 -2
  42. package/src/config.ts +148 -10
  43. package/src/generated/compatibility-version.json +2572 -0
  44. package/src/images/loop.ts +1 -1
  45. package/src/lab/artifacts/sanitize.ts +3 -3
  46. package/src/lab/automation/budgets.ts +78 -0
  47. package/src/lab/automation/config-persistence.ts +256 -0
  48. package/src/lab/automation/constants.ts +39 -0
  49. package/src/lab/automation/cooldown.ts +103 -0
  50. package/src/lab/automation/dispatch.ts +211 -0
  51. package/src/lab/automation/index.ts +13 -0
  52. package/src/lab/automation/orchestrator.ts +480 -0
  53. package/src/lab/automation/persistence.ts +512 -0
  54. package/src/lab/automation/planner.ts +371 -0
  55. package/src/lab/automation/policy.ts +136 -0
  56. package/src/lab/automation/queue.ts +191 -0
  57. package/src/lab/automation/recovery.ts +24 -0
  58. package/src/lab/automation/route-context.ts +21 -0
  59. package/src/lab/automation/run-key.ts +44 -0
  60. package/src/lab/automation/runs-query.ts +34 -0
  61. package/src/lab/automation/types.ts +160 -0
  62. package/src/lab/conformance/types.ts +23 -0
  63. package/src/lab/events/types.ts +2 -1
  64. package/src/lab/events/validate.ts +2 -1
  65. package/src/lab/fabric/constants.ts +40 -0
  66. package/src/lab/fabric/executor.ts +492 -0
  67. package/src/lab/fabric/index.ts +80 -0
  68. package/src/lab/fabric/manifest.ts +222 -0
  69. package/src/lab/fabric/observe.ts +454 -0
  70. package/src/lab/fabric/patch.ts +79 -0
  71. package/src/lab/fabric/producer-child.ts +139 -0
  72. package/src/lab/fabric/producer-isolate.ts +276 -0
  73. package/src/lab/fabric/producer-protocol.ts +61 -0
  74. package/src/lab/fabric/scratch.ts +439 -0
  75. package/src/lab/fabric/subject.ts +106 -0
  76. package/src/lab/fabric/types.ts +134 -0
  77. package/src/lab/fabric/verifier.ts +98 -0
  78. package/src/lab/index.ts +2 -0
  79. package/src/lab/ledger/store.ts +173 -0
  80. package/src/lab/live/executor.ts +29 -4
  81. package/src/lab/live/transport.ts +3 -1
  82. package/src/lab/observe/from-conformance.ts +12 -57
  83. package/src/lab/observe/from-live.ts +9 -1
  84. package/src/lab/paths.ts +74 -9
  85. package/src/lab/projection/schema.ts +14 -1
  86. package/src/lab/projection/verdicts.ts +89 -53
  87. package/src/lab/projection/verification.ts +83 -3
  88. package/src/lab/query/catalog.ts +34 -5
  89. package/src/lab/query/freshness.ts +53 -0
  90. package/src/lab/query/index.ts +11 -0
  91. package/src/lab/query/latest-observation.ts +59 -0
  92. package/src/lab/query/passive-production.ts +159 -0
  93. package/src/lab/subject/installation-salt.ts +24 -4
  94. package/src/lab/subject/protocol-subject.ts +80 -0
  95. package/src/lab/subject/route-subject.ts +2 -1
  96. package/src/lib/fabric-task-execution-authority.ts +7 -0
  97. package/src/lib/fabric-task-host.ts +29 -0
  98. package/src/lib/lab-live-route-production.ts +130 -0
  99. package/src/lib/local-management-capability.ts +100 -0
  100. package/src/lib/server-resource-ownership.ts +71 -0
  101. package/src/lib/windows-elevation.ts +8 -1
  102. package/src/lib/windows-user-principal.ts +62 -2
  103. package/src/lib/winsw.ts +8 -2
  104. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  105. package/src/oauth/account-import/index.ts +15 -0
  106. package/src/oauth/account-import/parser.ts +83 -0
  107. package/src/oauth/account-import/registry.ts +18 -0
  108. package/src/oauth/account-import/service.ts +75 -0
  109. package/src/oauth/account-import/types.ts +91 -0
  110. package/src/oauth/anthropic.ts +12 -1
  111. package/src/oauth/callback-server.ts +8 -2
  112. package/src/oauth/chatgpt.ts +12 -1
  113. package/src/oauth/google-antigravity.ts +27 -0
  114. package/src/oauth/health.ts +12 -48
  115. package/src/oauth/index.ts +122 -0
  116. package/src/oauth/kimi.ts +16 -2
  117. package/src/oauth/nous.ts +749 -0
  118. package/src/oauth/store.ts +56 -3
  119. package/src/providers/codex-capacity.ts +1 -0
  120. package/src/providers/registry.ts +161 -16
  121. package/src/responses/custom-tool-compat.ts +182 -0
  122. package/src/responses/parser.ts +22 -0
  123. package/src/responses/reasoning-replay-cache.ts +168 -11
  124. package/src/responses/schema.ts +1 -1
  125. package/src/router.ts +5 -20
  126. package/src/routing/capability.ts +9 -3
  127. package/src/routing/compatibility/assemble.ts +159 -0
  128. package/src/routing/compatibility/behavior.ts +224 -0
  129. package/src/routing/compatibility/catalog.ts +99 -0
  130. package/src/routing/compatibility/endpoint.ts +52 -0
  131. package/src/routing/compatibility/policy.ts +181 -0
  132. package/src/routing/compatibility/reader.ts +110 -0
  133. package/src/routing/compatibility/subject.ts +191 -0
  134. package/src/routing/compatibility/types.ts +64 -0
  135. package/src/routing/compatibility/version.ts +104 -0
  136. package/src/routing/evaluator.ts +27 -32
  137. package/src/routing/profile.ts +118 -4
  138. package/src/routing/trace.ts +82 -28
  139. package/src/server/auth-cors.ts +7 -0
  140. package/src/server/background-lifecycle.ts +20 -7
  141. package/src/server/chat-completions.ts +2 -2
  142. package/src/server/claude-messages.ts +6 -3
  143. package/src/server/direct-local-http.ts +343 -0
  144. package/src/server/index.ts +80 -18
  145. package/src/server/lifecycle.ts +12 -4
  146. package/src/server/local-management-read-client.ts +90 -0
  147. package/src/server/management/agent-settings-routes.ts +27 -3
  148. package/src/server/management/lab-automation-routes.ts +206 -0
  149. package/src/server/management/lab-routes.ts +13 -0
  150. package/src/server/management/logs-usage-routes.ts +1 -0
  151. package/src/server/management/oauth-account-routes.ts +57 -1
  152. package/src/server/management/provider-routes.ts +16 -0
  153. package/src/server/management/routing-profile-routes.ts +12 -24
  154. package/src/server/management-api.ts +2 -0
  155. package/src/server/management-auth.ts +68 -3
  156. package/src/server/proxy-liveness.ts +3 -2
  157. package/src/server/request-decompress.ts +9 -3
  158. package/src/server/request-log.ts +53 -4
  159. package/src/server/responses/collaboration.ts +93 -17
  160. package/src/server/responses/compact.ts +3 -0
  161. package/src/server/responses/core.ts +202 -15
  162. package/src/server/responses/policy-fallback.ts +1 -0
  163. package/src/server/responses-custom-tool-repair.ts +280 -0
  164. package/src/server/responses-terminal-repair.ts +342 -0
  165. package/src/server/startup-action-control.ts +8 -1
  166. package/src/service.ts +278 -12
  167. package/src/types.ts +49 -3
  168. package/src/usage/expected-prices.ts +30 -0
  169. package/src/usage/log.ts +30 -4
  170. package/src/usage/summary.ts +153 -2
  171. package/src/web-search/loop.ts +1 -1
  172. package/gui/dist/assets/index-Cw1_mxO8.js +0 -70
  173. package/src/cli/internal-dispatch.ts +0 -20
@@ -8,6 +8,13 @@ import {
8
8
  resolveEnvValue,
9
9
  } from "../../config";
10
10
  import { parseRequest } from "../../responses/parser";
11
+ import {
12
+ bindReasoningReplayScope,
13
+ reasoningReplayCodexCredentialIdentity,
14
+ reasoningReplayDestinationIdentity,
15
+ reasoningReplayKeyCredentialIdentity,
16
+ reasoningReplayOAuthCredentialIdentity,
17
+ } from "../../responses/reasoning-replay-cache";
11
18
  import { buildCompactV1Output, COMPACT_PROMPT, decodeCompactionSummary, extractCompactUserMessages } from "../../responses/compaction";
12
19
  import { FORWARD_HEADERS, sanitizeReasoningInputContent } from "../../adapters/openai-responses";
13
20
  import {
@@ -24,6 +31,7 @@ import {
24
31
  type RouteResult,
25
32
  } from "../../router";
26
33
  import { evidenceFromBody } from "../../routing/request-evidence";
34
+ import { resolveProductionRouteSubject } from "../../routing/compatibility/subject";
27
35
  import {
28
36
  advanceComboAfterFailure,
29
37
  comboDefaultEffort,
@@ -94,6 +102,7 @@ import {
94
102
  recordCodexUpstreamOutcome,
95
103
  type CodexUpstreamOutcome,
96
104
  } from "../../codex/routing";
105
+ import { codexAuthContextLogLabel } from "../../codex/account-label";
97
106
  import {
98
107
  applyUpstreamRecoveryInit,
99
108
  fetchWithResetRetry,
@@ -109,7 +118,11 @@ import { applyOpenAiVirtualModel, resolveOpenAiCompactModel } from "../../provid
109
118
  import { isUsageDebugEnabled } from "../../usage/debug";
110
119
  import { readJsonRequestBody, DecompressedBodyTooLargeError, UnsupportedContentEncodingError } from "../request-decompress";
111
120
  import { resolveAdapter, resolveWireProtocolOverride } from "../adapter-resolve";
112
- import { providerModelResponsesUpstreamStreaming, type InboundWire } from "../../providers/registry";
121
+ import {
122
+ providerModelResponsesTerminalRepair,
123
+ providerModelResponsesUpstreamStreaming,
124
+ type InboundWire,
125
+ } from "../../providers/registry";
113
126
  import type { AdapterRequest } from "../../adapters/base";
114
127
  import {
115
128
  hasKeyPoolFailover,
@@ -165,6 +178,10 @@ import {
165
178
  sanitizePassthroughHeaders,
166
179
  } from "../relay";
167
180
  import { relaySseEagerBounded } from "../relay-eager";
181
+ import {
182
+ relayResponsesSseWithTerminalRepair,
183
+ type ResponsesTerminalRepairScheduler,
184
+ } from "../responses-terminal-repair";
168
185
  import { isWin32EagerRewrite, selectEagerPath } from "../../lib/bun-stream-caps";
169
186
  import { cancelBodyOnAbort } from "../../lib/abort";
170
187
  import {
@@ -205,6 +222,8 @@ import {
205
222
  payloadRewriteAsBlockRewrite,
206
223
  relaySseWithBlockRewrite,
207
224
  } from "../sse-payload-rewrite";
225
+ import { collectRoutedCustomToolNames, restoreRoutedCustomCallsInJson } from "../../responses/custom-tool-compat";
226
+ import { createRoutedCustomToolRestoreBlockRewrite } from "../responses-custom-tool-repair";
208
227
  import { createGithubCopilotResponsesBlockRewrite } from "../github-copilot-responses-repair";
209
228
  import { responsesJsonToSseStream } from "../responses-json-events";
210
229
  import { guardTerminalEventStream } from "./terminal-guard";
@@ -244,6 +263,58 @@ export function codexLogAccountId(authCtx: CodexAuthContext): string | null {
244
263
  return authCtx.kind === "pool" || authCtx.kind === "main-pool" ? authCtx.accountId : null;
245
264
  }
246
265
 
266
+ function bindRouteReasoningReplayScope(args: {
267
+ parsed: OcxParsedRequest;
268
+ providerName: string;
269
+ provider: OcxProviderConfig;
270
+ adapterName: string;
271
+ oauthCredentialSnapshot?: Pick<OAuthAccessSnapshot, "accountId" | "generation">;
272
+ codexAuthContext?: CodexAuthContext;
273
+ forwardHeaders?: Headers;
274
+ }): void {
275
+ const { parsed, providerName, provider, adapterName } = args;
276
+ let credentialIdentity: string | undefined;
277
+ if (provider.authMode === "oauth") {
278
+ credentialIdentity = reasoningReplayOAuthCredentialIdentity(
279
+ args.oauthCredentialSnapshot,
280
+ provider.headers,
281
+ );
282
+ } else if (provider.authMode === "forward") {
283
+ const poolContext = args.codexAuthContext?.kind === "pool"
284
+ || args.codexAuthContext?.kind === "main-pool"
285
+ ? args.codexAuthContext
286
+ : undefined;
287
+ credentialIdentity = reasoningReplayCodexCredentialIdentity({
288
+ authorization: poolContext
289
+ ? `Bearer ${poolContext.accessToken}`
290
+ : args.forwardHeaders?.get("authorization"),
291
+ chatgptAccountId: poolContext?.chatgptAccountId
292
+ ?? args.forwardHeaders?.get("chatgpt-account-id"),
293
+ accountId: poolContext?.accountId,
294
+ credentialGeneration: poolContext?.kind === "pool"
295
+ ? poolContext.generation
296
+ : undefined,
297
+ writerGeneration: poolContext?.writerGeneration,
298
+ headers: provider.headers,
299
+ });
300
+ } else if (provider.authMode !== "local") {
301
+ credentialIdentity = reasoningReplayKeyCredentialIdentity(provider);
302
+ }
303
+ const providerDestinationIdentity = reasoningReplayDestinationIdentity(provider.baseUrl);
304
+ bindReasoningReplayScope(
305
+ parsed._reasoningReplayScope,
306
+ credentialIdentity && providerDestinationIdentity
307
+ ? {
308
+ providerName,
309
+ providerDestinationIdentity,
310
+ adapterName,
311
+ modelId: parsed.modelId,
312
+ credentialIdentity,
313
+ }
314
+ : undefined,
315
+ );
316
+ }
317
+
247
318
  function isFixedCodexAccount(authCtx: CodexAuthContext): boolean {
248
319
  return (authCtx.kind === "pool" || authCtx.kind === "main-pool")
249
320
  && authCtx.fixedAccount === true;
@@ -470,6 +541,14 @@ async function retryCodexPoolOnAlternateAccount(
470
541
  resolveWireProtocolOverride(route.providerName, route.modelId, retryProvider, inboundWire),
471
542
  config.cacheRetention,
472
543
  );
544
+ bindRouteReasoningReplayScope({
545
+ parsed,
546
+ providerName: route.providerName,
547
+ provider: retryProvider,
548
+ adapterName: retryAdapter.name,
549
+ codexAuthContext: retryAuthCtx,
550
+ forwardHeaders: retryHeaders,
551
+ });
473
552
  const request = await retryAdapter.buildRequest(parsed, {
474
553
  headers: retryHeaders,
475
554
  translatorBudget: options.translatorBudget,
@@ -484,6 +563,13 @@ async function retryCodexPoolOnAlternateAccount(
484
563
  retryAuthCtx.accountId,
485
564
  config,
486
565
  );
566
+ logCtx.accountLogLabel = codexAuthContextLogLabel(retryAuthCtx, config);
567
+ sealRequestAttemptIdentity(
568
+ logCtx.activeAttempt,
569
+ logCtx.provider,
570
+ retryAdapter.name,
571
+ logCtx.accountLogLabel,
572
+ );
487
573
 
488
574
  noteAttemptSend(logCtx.activeAttempt, passthroughEstimate);
489
575
  let upstreamResponse: Response;
@@ -636,6 +722,8 @@ export interface HandleResponsesOptions {
636
722
  setTerminalOutcomeRecorder?: (recorder: ((status: ResponsesTerminalStatus, httpStatusOverride?: number) => void) | undefined) => void;
637
723
  onNativePassthroughTerminal?: (status: ResponsesTerminalStatus) => void;
638
724
  onNativePassthroughCancel?: () => void;
725
+ /** Internal deterministic clock/timer seam for provider terminal repair. */
726
+ responsesTerminalRepairScheduler?: ResponsesTerminalRepairScheduler;
639
727
  /**
640
728
  * When true, body `prompt_cache_key` is a Claude Desktop shared cache cohort
641
729
  * (system/tools hash), not a per-session id — do not use it for Anthropic pool affinity.
@@ -1157,6 +1245,7 @@ export async function handleComboResponses(
1157
1245
  attempt,
1158
1246
  childLog.provider,
1159
1247
  childLog.providerAdapter ?? attempt.adapter,
1248
+ childLog.accountLogLabel,
1160
1249
  );
1161
1250
  finishRequestAttempt(attempt, 499, Date.now() - started, childLog.usage);
1162
1251
  (logCtx.attempts ??= []).push(attempt);
@@ -1211,6 +1300,7 @@ export async function handleComboResponses(
1211
1300
  attempt,
1212
1301
  childLog.provider,
1213
1302
  childLog.providerAdapter ?? attempt.adapter,
1303
+ childLog.accountLogLabel,
1214
1304
  );
1215
1305
  (logCtx.attempts ??= []).push(attempt);
1216
1306
  attemptRetained = true;
@@ -1256,6 +1346,7 @@ export async function handleComboResponses(
1256
1346
  attempt,
1257
1347
  childLog.provider,
1258
1348
  childLog.providerAdapter ?? attempt.adapter,
1349
+ childLog.accountLogLabel,
1259
1350
  );
1260
1351
  finishRequestAttempt(
1261
1352
  attempt,
@@ -1441,7 +1532,10 @@ async function handleResponsesInner(
1441
1532
  parsed._providerContinuation = previousResponseProviderState(parsed.previousResponseId);
1442
1533
  parsed._cursorConversationId = parsed._providerContinuation?.cursor?.conversationId;
1443
1534
  const clientThreadId = req.headers.get("x-codex-parent-thread-id")?.trim();
1444
- if (clientThreadId) parsed._clientThreadId = clientThreadId;
1535
+ if (clientThreadId) {
1536
+ parsed._clientThreadId = clientThreadId;
1537
+ parsed._reasoningReplayScope = { clientThreadId };
1538
+ }
1445
1539
  } catch (err) {
1446
1540
  if (isTranslatorBudgetExceededError(err)) {
1447
1541
  return formatErrorResponse(413, "request_too_large", "request translation buffer exceeded the safe limit", {
@@ -1653,6 +1747,7 @@ async function handleResponsesInner(
1653
1747
  logCtx.provider = route.codexAccountNamespace
1654
1748
  ? `${route.providerName}-${route.codexAccountNamespace}`
1655
1749
  : formatCodexProviderForLog(route.providerName, codexLogAccountId(authCtx), config);
1750
+ logCtx.accountLogLabel = codexAuthContextLogLabel(authCtx, config);
1656
1751
  // Prefer Codex pool account as the Cursor thread namespace when present. Cursor routes without
1657
1752
  // codexAccountMode still get a credential-derived scope inside the Cursor adapter.
1658
1753
  const identityScope = codexLogAccountId(authCtx);
@@ -1666,6 +1761,7 @@ async function handleResponsesInner(
1666
1761
  const isOAuth401ReplayProvider = (route.providerName === "xai" || route.providerName === "github-copilot" || route.providerName === "kiro")
1667
1762
  && route.provider.authMode === "oauth";
1668
1763
  let sentOAuthSnapshot: OAuthAccessSnapshot | undefined;
1764
+ let replayOAuthCredentialSnapshot: Pick<OAuthAccessSnapshot, "accountId" | "generation"> | undefined;
1669
1765
  let anthropicPoolAccountId: string | null = null;
1670
1766
  let anthropicPoolFailovers = 0;
1671
1767
  const anthropicSessionKey = route.providerName === "anthropic" && route.provider.authMode === "oauth"
@@ -1701,6 +1797,10 @@ async function handleResponsesInner(
1701
1797
  logCtx.provider = formatAnthropicProviderForLog("anthropic", selection.accountId, config);
1702
1798
  } else {
1703
1799
  const resolved = await getValidAccessTokenSnapshot(route.providerName);
1800
+ replayOAuthCredentialSnapshot = {
1801
+ accountId: resolved.accountId,
1802
+ generation: resolved.generation,
1803
+ };
1704
1804
  if (isOAuth401ReplayProvider) sentOAuthSnapshot = resolved;
1705
1805
  route.provider = { ...route.provider, apiKey: resolved.accessToken };
1706
1806
  if (route.providerName === "kiro") {
@@ -1750,8 +1850,18 @@ async function handleResponsesInner(
1750
1850
  delete route.codexAccountId;
1751
1851
  delete route.codexAccountNamespace;
1752
1852
  logCtx.provider = route.providerName;
1853
+ delete logCtx.accountLogLabel;
1753
1854
  }
1754
1855
  const adapter = resolveAdapter(adapterProvider, config.cacheRetention);
1856
+ bindRouteReasoningReplayScope({
1857
+ parsed,
1858
+ providerName: route.providerName,
1859
+ provider: adapterProvider,
1860
+ adapterName: adapter.name,
1861
+ oauthCredentialSnapshot: replayOAuthCredentialSnapshot,
1862
+ codexAuthContext: authCtx,
1863
+ forwardHeaders: selectedForwardHeaders,
1864
+ });
1755
1865
  logCtx.providerAdapter = adapter.name;
1756
1866
  // Ordinary requests receive one durable attempt only after their final initial
1757
1867
  // adapter is resolved. Combo children own their attempt and retries keep it.
@@ -1766,7 +1876,24 @@ async function handleResponsesInner(
1766
1876
  logCtx.activeAttemptStartedAt = Date.now();
1767
1877
  (logCtx.attempts ??= []).push(attempt);
1768
1878
  }
1769
- sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name);
1879
+ sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name, logCtx.accountLogLabel);
1880
+ // CL-09: attach only the opaque exact route-subject identity to the attempt.
1881
+ // This is best-effort passive metadata: no Lab state is created and failure
1882
+ // must never alter, retry, or delay the upstream request.
1883
+ if (logCtx.activeAttempt && !logCtx.activeAttempt.labRouteSubjectId) {
1884
+ try {
1885
+ const passiveSubject = resolveProductionRouteSubject(
1886
+ config,
1887
+ route.providerName,
1888
+ route.modelId,
1889
+ route.provider,
1890
+ inboundWire,
1891
+ );
1892
+ if (passiveSubject) logCtx.activeAttempt.labRouteSubjectId = passiveSubject.subjectId;
1893
+ } catch {
1894
+ // Omit passive linkage when exact subject construction is unavailable.
1895
+ }
1896
+ }
1770
1897
  const isPassthrough = "passthrough" in adapter && !!adapter.passthrough;
1771
1898
 
1772
1899
  if (adapter.name === "kiro" && parsed.previousResponseId && !parsed._previousResponseInputExpanded) {
@@ -1889,6 +2016,9 @@ async function handleResponsesInner(
1889
2016
  const imageGenCallAliases = route.provider.authMode === "forward"
1890
2017
  ? new Map<string, { namespace: string; name: string }>()
1891
2018
  : imageGenToolCallAliases(toolBridgeMaps.toolNsMap, parsed._rawBody, translatorBudget);
2019
+ const routedCustomToolNames = route.provider.authMode === "forward"
2020
+ ? new Set<string>()
2021
+ : collectRoutedCustomToolNames(parsed._rawBody);
1892
2022
  // Local continuation cache for the ChatGPT passthrough. Codex WS turns chain with
1893
2023
  // previous_response_id, ocx converts them to internal HTTP requests, and the ChatGPT Codex
1894
2024
  // REST backend rejects the parameter — the adapter strips it in forward mode, so the ONLY
@@ -2249,6 +2379,20 @@ async function handleResponsesInner(
2249
2379
  // devlog/_fin/260731_macos_rss_retention/100_darwin_eager_optin.md).
2250
2380
  // The bundled known-bad runtime remains on tee by default on both platforms.
2251
2381
  if (isEventStream && upstreamResponse.body) {
2382
+ const terminalRepairPolicy = providerModelResponsesTerminalRepair(
2383
+ route.providerName,
2384
+ route.provider,
2385
+ route.modelId,
2386
+ );
2387
+ const passthroughSseBody = terminalRepairPolicy
2388
+ ? relayResponsesSseWithTerminalRepair(
2389
+ upstreamResponse.body,
2390
+ upstream,
2391
+ terminalRepairPolicy,
2392
+ translatorBudget,
2393
+ options.responsesTerminalRepairScheduler,
2394
+ )
2395
+ : upstreamResponse.body;
2252
2396
  const repairConfig = route.provider.responsesItemIdRepair;
2253
2397
  const snapshotRepairEnabled = hasResponsesSnapshotRepair(route.provider.responsesSnapshotRepair);
2254
2398
  const githubCopilotRepairEnabled = route.providerName === "github-copilot";
@@ -2279,6 +2423,9 @@ async function handleResponsesInner(
2279
2423
  payloadRewrites.length > 0
2280
2424
  ? payloadRewriteAsBlockRewrite(composeSsePayloadRewrites(...payloadRewrites))
2281
2425
  : undefined,
2426
+ routedCustomToolNames.size > 0
2427
+ ? createRoutedCustomToolRestoreBlockRewrite(routedCustomToolNames, translatorBudget)
2428
+ : undefined,
2282
2429
  githubCopilotRepairEnabled
2283
2430
  ? createGithubCopilotResponsesBlockRewrite(translatorBudget)
2284
2431
  : undefined,
@@ -2334,7 +2481,7 @@ async function handleResponsesInner(
2334
2481
  onFirstOutput: options.onFirstOutput,
2335
2482
  pinCompletedResponseIdToFirstSeen: githubCopilotRepairEnabled,
2336
2483
  });
2337
- const eagerBody = relaySseEagerBounded(upstreamResponse.body, turnAc, {
2484
+ const eagerBody = relaySseEagerBounded(passthroughSseBody, turnAc, {
2338
2485
  inspectChunk: chunk => inspector.feed(chunk),
2339
2486
  finishInspection: () => inspector.finish(),
2340
2487
  disposeInspection: () => inspector.dispose(),
@@ -2370,7 +2517,7 @@ async function handleResponsesInner(
2370
2517
  })),
2371
2518
  );
2372
2519
  }
2373
- const [nativeBody, inspectBody] = upstreamResponse.body.tee();
2520
+ const [nativeBody, inspectBody] = passthroughSseBody.tee();
2374
2521
  const turnAc = new AbortController();
2375
2522
  const clientGone = new AbortController();
2376
2523
  linkAbortSignal(upstream, turnAc.signal);
@@ -2463,7 +2610,10 @@ async function handleResponsesInner(
2463
2610
  } catch { /* non-JSON despite content-type; recording is best-effort */ }
2464
2611
  }
2465
2612
  const clientJson = (() => {
2466
- const restored = restoreImageGenCallsInJson(text, imageGenCallAliases);
2613
+ const restored = restoreRoutedCustomCallsInJson(
2614
+ restoreImageGenCallsInJson(text, imageGenCallAliases),
2615
+ routedCustomToolNames,
2616
+ );
2467
2617
  const repaired = (() => {
2468
2618
  if (!hasResponsesSnapshotRepair(route.provider.responsesSnapshotRepair)) return restored;
2469
2619
  let outbound: unknown;
@@ -2673,10 +2823,17 @@ async function handleResponsesInner(
2673
2823
  });
2674
2824
  if (!rotated) return null;
2675
2825
  route.provider = rotated;
2676
- return resolveAdapter(
2826
+ const rotatedAdapter = resolveAdapter(
2677
2827
  resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire),
2678
2828
  config.cacheRetention,
2679
2829
  );
2830
+ bindRouteReasoningReplayScope({
2831
+ parsed,
2832
+ providerName: route.providerName,
2833
+ provider: route.provider,
2834
+ adapterName: rotatedAdapter.name,
2835
+ });
2836
+ return rotatedAdapter;
2680
2837
  },
2681
2838
  retryOn429Policy: rateLimitRetryPolicyFor(route.provider),
2682
2839
  ...(options.onFirstOutput ? { onFirstOutput: options.onFirstOutput } : {}),
@@ -2743,10 +2900,17 @@ async function handleResponsesInner(
2743
2900
  });
2744
2901
  if (!rotated) return null;
2745
2902
  route.provider = rotated;
2746
- return resolveAdapter(
2903
+ const rotatedAdapter = resolveAdapter(
2747
2904
  resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire),
2748
2905
  config.cacheRetention,
2749
2906
  );
2907
+ bindRouteReasoningReplayScope({
2908
+ parsed,
2909
+ providerName: route.providerName,
2910
+ provider: route.provider,
2911
+ adapterName: rotatedAdapter.name,
2912
+ });
2913
+ return rotatedAdapter;
2750
2914
  },
2751
2915
  retryOn429Policy: rateLimitRetryPolicyFor(route.provider),
2752
2916
  });
@@ -2813,7 +2977,7 @@ async function handleResponsesInner(
2813
2977
  }, 2_000,
2814
2978
  {
2815
2979
  translatorBudget,
2816
- replayCacheScope: parsed._clientThreadId ?? "global",
2980
+ replayCacheScope: parsed._reasoningReplayScope,
2817
2981
  ...(options.forceEmptyResponseId ? { responseId: "" } : {}),
2818
2982
  stallTimeoutSec: config.stallTimeoutSec,
2819
2983
  hideThinkingSummary: parsed.options.hideThinkingSummary,
@@ -2860,7 +3024,7 @@ async function handleResponsesInner(
2860
3024
  let providerState: OcxProviderContinuationState | undefined;
2861
3025
  const json = buildResponseJSON(events, parsed._responseModelId ?? parsed.modelId, {
2862
3026
  translatorBudget,
2863
- replayCacheScope: parsed._clientThreadId ?? "global",
3027
+ replayCacheScope: parsed._reasoningReplayScope,
2864
3028
  hideThinkingSummary: parsed.options.hideThinkingSummary,
2865
3029
  toolNsMap,
2866
3030
  freeformToolNames,
@@ -3024,7 +3188,7 @@ async function handleResponsesInner(
3024
3188
  : undefined;
3025
3189
  if (retryEstimate !== undefined) logCtx.usageLogInputTokens = retryEstimate;
3026
3190
  logCtx.providerAdapter = activeAdapter.name;
3027
- sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, activeAdapter.name);
3191
+ sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, activeAdapter.name, logCtx.accountLogLabel);
3028
3192
  noteAttemptSend(logCtx.activeAttempt, retryEstimate, recovery);
3029
3193
  try {
3030
3194
  try {
@@ -3063,6 +3227,10 @@ async function handleResponsesInner(
3063
3227
  return formatErrorResponse(401, "authentication_error", err instanceof Error ? err.message : String(err));
3064
3228
  }
3065
3229
  sentOAuthSnapshot = refreshed;
3230
+ replayOAuthCredentialSnapshot = {
3231
+ accountId: refreshed.accountId,
3232
+ generation: refreshed.generation,
3233
+ };
3066
3234
  if (route.providerName === "kiro") {
3067
3235
  parsed._kiroAuthContext = { ...(refreshed.kiro ?? {}) };
3068
3236
  }
@@ -3078,6 +3246,13 @@ async function handleResponsesInner(
3078
3246
  resolveWireProtocolOverride(route.providerName, route.modelId, refreshedProvider, inboundWire),
3079
3247
  config.cacheRetention,
3080
3248
  );
3249
+ bindRouteReasoningReplayScope({
3250
+ parsed,
3251
+ providerName: route.providerName,
3252
+ provider: refreshedProvider,
3253
+ adapterName: activeAdapter.name,
3254
+ oauthCredentialSnapshot: replayOAuthCredentialSnapshot,
3255
+ });
3081
3256
  const result = await rebuildAndRefetch("oauth-401");
3082
3257
  if ("failed" in result) return result.failed;
3083
3258
  upstreamResponse = result;
@@ -3143,6 +3318,12 @@ async function handleResponsesInner(
3143
3318
  resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire),
3144
3319
  config.cacheRetention,
3145
3320
  );
3321
+ bindRouteReasoningReplayScope({
3322
+ parsed,
3323
+ providerName: route.providerName,
3324
+ provider: route.provider,
3325
+ adapterName: activeAdapter.name,
3326
+ });
3146
3327
  const result = await rebuildAndRefetch("key-429");
3147
3328
  if ("failed" in result) return result.failed;
3148
3329
  upstreamResponse = result;
@@ -3176,7 +3357,7 @@ async function handleResponsesInner(
3176
3357
  resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire),
3177
3358
  config.cacheRetention,
3178
3359
  );
3179
- sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, activeAdapter.name);
3360
+ sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, activeAdapter.name, logCtx.accountLogLabel);
3180
3361
  const result = await rebuildAndRefetch("anthropic-oauth-429");
3181
3362
  if ("failed" in result) return result.failed;
3182
3363
  upstreamResponse = result;
@@ -3414,6 +3595,12 @@ async function handleResponsesInner(
3414
3595
  resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire),
3415
3596
  config.cacheRetention,
3416
3597
  );
3598
+ bindRouteReasoningReplayScope({
3599
+ parsed,
3600
+ providerName: route.providerName,
3601
+ provider: route.provider,
3602
+ adapterName: activeAdapter.name,
3603
+ });
3417
3604
  nextContinuationRecoveryKind = "key-429";
3418
3605
  continue;
3419
3606
  }
@@ -3444,7 +3631,7 @@ async function handleResponsesInner(
3444
3631
  resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire),
3445
3632
  config.cacheRetention,
3446
3633
  );
3447
- sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, activeAdapter.name);
3634
+ sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, activeAdapter.name, logCtx.accountLogLabel);
3448
3635
  nextContinuationRecoveryKind = "anthropic-oauth-429";
3449
3636
  continue;
3450
3637
  } catch {
@@ -3519,7 +3706,7 @@ async function handleResponsesInner(
3519
3706
  () => upstream.abort(), 2_000,
3520
3707
  {
3521
3708
  translatorBudget,
3522
- replayCacheScope: parsed._clientThreadId ?? "global",
3709
+ replayCacheScope: parsed._reasoningReplayScope,
3523
3710
  ...(options.forceEmptyResponseId ? { responseId: "" } : {}),
3524
3711
  stallTimeoutSec: config.stallTimeoutSec,
3525
3712
  hideThinkingSummary: parsed.options.hideThinkingSummary,
@@ -3577,7 +3764,7 @@ async function handleResponsesInner(
3577
3764
  let providerState: OcxProviderContinuationState | undefined;
3578
3765
  const json = buildResponseJSON(events, parsed._responseModelId ?? parsed.modelId, {
3579
3766
  translatorBudget,
3580
- replayCacheScope: parsed._clientThreadId ?? "global",
3767
+ replayCacheScope: parsed._reasoningReplayScope,
3581
3768
  hideThinkingSummary: parsed.options.hideThinkingSummary,
3582
3769
  toolNsMap,
3583
3770
  freeformToolNames,
@@ -96,6 +96,7 @@ function finishFailedPolicyAttempt(logCtx: RequestLogContext, status: number): v
96
96
  delete logCtx.usageFromBridge;
97
97
  delete logCtx.upstreamError;
98
98
  delete logCtx.terminalHttpStatus;
99
+ delete logCtx.terminalErrorCode;
99
100
  delete logCtx.terminalIncompleteReason;
100
101
  }
101
102