@bitkyc08/opencodex 2.86.0 → 2.87.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 (378) hide show
  1. package/gui/dist/assets/App-ZfYbLHkB.js +58 -0
  2. package/gui/dist/assets/App-wT3mXqFz.css +1 -0
  3. package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-DCd-A1az.js} +1 -1
  4. package/gui/dist/assets/index-CfYDMj34.js +94 -0
  5. package/gui/dist/assets/index-DPeB9HpV.css +1 -0
  6. package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/package.json +1 -1
  9. package/src/adapters/anthropic/beta-allowlist.ts +9 -8
  10. package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
  11. package/src/adapters/anthropic/passthrough.ts +7 -23
  12. package/src/adapters/anthropic-progress.ts +84 -0
  13. package/src/adapters/anthropic.ts +108 -21
  14. package/src/adapters/base.ts +2 -0
  15. package/src/adapters/claude-cli/adapter.ts +111 -23
  16. package/src/adapters/claude-cli/profiles.ts +2 -1
  17. package/src/adapters/claude-cli/stable-replay.ts +108 -0
  18. package/src/adapters/claude-cli/usage-admission.ts +230 -0
  19. package/src/adapters/coding-agent/protocol.ts +1 -1
  20. package/src/adapters/coding-agent/turn.ts +18 -2
  21. package/src/adapters/dsh-account.ts +97 -0
  22. package/src/adapters/google.ts +35 -8
  23. package/src/adapters/openai-chat/messages.ts +4 -3
  24. package/src/adapters/openai-chat/passthrough.ts +8 -2
  25. package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
  26. package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
  27. package/src/adapters/openai-responses/passthrough.ts +57 -125
  28. package/src/adapters/openai-responses/request-strips.ts +25 -3
  29. package/src/adapters/openai-responses/transport.ts +81 -0
  30. package/src/adapters/qoder/adapter.ts +4 -2
  31. package/src/adapters/registry.ts +5 -0
  32. package/src/adapters/zed.ts +26 -1
  33. package/src/advisor/activity.ts +55 -0
  34. package/src/advisor/budget.ts +62 -0
  35. package/src/advisor/consult.ts +4 -1
  36. package/src/advisor/context.ts +16 -4
  37. package/src/advisor/evidence-tool.ts +21 -0
  38. package/src/advisor/evidence.ts +117 -0
  39. package/src/advisor/runtime.ts +79 -31
  40. package/src/advisor/settings.ts +11 -2
  41. package/src/bridge/errors.ts +19 -1
  42. package/src/bridge/internal.ts +3 -1
  43. package/src/bridge/response-json.ts +18 -3
  44. package/src/bridge/sse.ts +58 -8
  45. package/src/bridge/terminal-refusal.ts +59 -0
  46. package/src/claude/desktop-3p-library.ts +32 -7
  47. package/src/claude/desktop-3p.ts +55 -13
  48. package/src/claude/desktop-first-party.ts +1 -1
  49. package/src/claude/desktop-picker-platform.ts +4 -0
  50. package/src/claude/desktop-picker.ts +15 -8
  51. package/src/claude/desktop-system-proxy.ts +50 -4
  52. package/src/claude/inbound-content-options.ts +11 -0
  53. package/src/claude/inbound.ts +33 -0
  54. package/src/claude/intercept/picker-ca-startup.ts +2 -1
  55. package/src/claude/intercept/picker-runtime.ts +6 -5
  56. package/src/claude/intercept/picker-trust-windows.ts +338 -0
  57. package/src/claude/intercept/picker-trust.ts +19 -0
  58. package/src/claude/intercept/port-policy.ts +30 -0
  59. package/src/claude/intercept/runtime.ts +97 -94
  60. package/src/claude/outbound.ts +38 -13
  61. package/src/cli/account-api.ts +1 -0
  62. package/src/cli/account-policy-dto.ts +6 -0
  63. package/src/cli/account-policy.ts +34 -5
  64. package/src/cli/account-preservation.ts +44 -0
  65. package/src/cli/account.ts +8 -2
  66. package/src/cli/advisor.ts +17 -3
  67. package/src/cli/capabilities-accounts.ts +20 -1
  68. package/src/cli/capabilities-agents-routing.ts +5 -2
  69. package/src/cli/capabilities-base.ts +38 -10
  70. package/src/cli/capabilities-observe-system.ts +63 -3
  71. package/src/cli/capabilities-provider-models.ts +11 -0
  72. package/src/cli/codex-cli-update.ts +151 -41
  73. package/src/cli/codex-provider.ts +44 -0
  74. package/src/cli/combo-input.ts +10 -1
  75. package/src/cli/combo-result.ts +1 -1
  76. package/src/cli/combo.ts +2 -1
  77. package/src/cli/cross-home-owner.ts +17 -14
  78. package/src/cli/dispatch.ts +25 -4
  79. package/src/cli/doctor.ts +6 -1
  80. package/src/cli/ensure-desktop-cold-start.ts +60 -7
  81. package/src/cli/help.ts +3 -2
  82. package/src/cli/index.ts +1 -1
  83. package/src/cli/integration-plan-dto.ts +1 -0
  84. package/src/cli/log-history.ts +172 -0
  85. package/src/cli/models-runtime-subcommands.ts +1 -0
  86. package/src/cli/models-runtime.ts +72 -8
  87. package/src/cli/observe-stream.ts +2 -0
  88. package/src/cli/observe.ts +16 -4
  89. package/src/cli/provider.ts +27 -5
  90. package/src/cli/registry.ts +28 -8
  91. package/src/cli/runtime-api.ts +5 -3
  92. package/src/cli/system-command.ts +40 -8
  93. package/src/cli/uninstall-client-state.ts +32 -0
  94. package/src/cli/usage-compare.ts +309 -0
  95. package/src/cli/usage-model-search.ts +4 -4
  96. package/src/cli/usage-report.ts +2 -2
  97. package/src/cli/v2.ts +1 -1
  98. package/src/client/machine-listener.ts +1 -3
  99. package/src/client/runtime.ts +1 -0
  100. package/src/clients/client-path.ts +51 -0
  101. package/src/clients/config-export/contracts.ts +2 -1
  102. package/src/clients/config-export.ts +18 -44
  103. package/src/clients/gentle-shell-path.ts +87 -0
  104. package/src/codex/account-auto-switch.ts +2 -0
  105. package/src/codex/account-identity.ts +29 -0
  106. package/src/codex/account-label.ts +1 -1
  107. package/src/codex/account-lifecycle.ts +21 -5
  108. package/src/codex/account-preservation.ts +28 -0
  109. package/src/codex/account-use-remaining.ts +23 -0
  110. package/src/codex/app-server-processes.ts +97 -7
  111. package/src/codex/app-server-restart-service.ts +55 -4
  112. package/src/codex/auth-api/account-list.ts +4 -0
  113. package/src/codex/auth-api/account-pause-group.ts +6 -50
  114. package/src/codex/auth-api/account-preservation.ts +57 -0
  115. package/src/codex/auth-api/routes.ts +8 -0
  116. package/src/codex/auth-api/use-remaining-route.ts +36 -0
  117. package/src/codex/auth-api.ts +0 -1
  118. package/src/codex/auth-context.ts +2 -2
  119. package/src/codex/catalog/auto-review.ts +20 -3
  120. package/src/codex/catalog/build-entries.ts +9 -1
  121. package/src/codex/catalog/combo-member.ts +14 -5
  122. package/src/codex/catalog/configured-tiers.ts +1 -1
  123. package/src/codex/catalog/gather-capture.ts +2 -0
  124. package/src/codex/catalog/metadata.ts +11 -9
  125. package/src/codex/catalog/model-hints.ts +21 -16
  126. package/src/codex/catalog/model-visibility.ts +3 -1
  127. package/src/codex/catalog/parsing.ts +30 -86
  128. package/src/codex/catalog/provider-models.ts +36 -34
  129. package/src/codex/catalog/routed-gather.ts +12 -13
  130. package/src/codex/cli-install-provenance.ts +105 -17
  131. package/src/codex/cli-update-plan.ts +545 -0
  132. package/src/codex/config-write-lock.ts +23 -2
  133. package/src/codex/desktop-app/picker-freshness.ts +96 -0
  134. package/src/codex/desktop-app-restart.ts +21 -0
  135. package/src/codex/desktop-compatibility/installed-build.ts +24 -6
  136. package/src/codex/desktop-compatibility/routing-preflight.ts +1 -1
  137. package/src/codex/desktop-compatibility/runtime.ts +12 -3
  138. package/src/codex/desktop-compatibility/windows-key-protection.ts +115 -9
  139. package/src/codex/features.ts +85 -45
  140. package/src/codex/inject/history-failure.ts +27 -0
  141. package/src/codex/inject/restore.ts +24 -2
  142. package/src/codex/inject/routing-classify.ts +4 -3
  143. package/src/codex/inject.ts +18 -27
  144. package/src/codex/main-account-hard-lock.ts +30 -1
  145. package/src/codex/native-profile-startup.ts +1 -1
  146. package/src/codex/ocx-compaction-history.ts +12 -10
  147. package/src/codex/paths.ts +86 -1
  148. package/src/codex/provider-routing.ts +174 -0
  149. package/src/codex/refresh.ts +2 -2
  150. package/src/codex/routing/health-store.ts +1 -1
  151. package/src/codex/routing/selection.ts +4 -3
  152. package/src/codex/routing/thread-affinity.ts +1 -1
  153. package/src/codex/signed-in-identity.ts +43 -0
  154. package/src/codex/subagent-model-fallback.ts +1 -1
  155. package/src/codex/sync.ts +6 -1
  156. package/src/codex/user-identity.ts +16 -5
  157. package/src/codex/windows-local-appdata.ts +84 -0
  158. package/src/combos/failover.ts +6 -1
  159. package/src/combos/index.ts +1 -0
  160. package/src/combos/resolve.ts +46 -10
  161. package/src/combos/types.ts +15 -0
  162. package/src/config/diagnostics.ts +33 -6
  163. package/src/config/live-reconcile.ts +38 -2
  164. package/src/config/load-degrade.ts +17 -0
  165. package/src/config/opencode-wire-upgrade.ts +54 -0
  166. package/src/config/opencode-zen-upgrade.ts +52 -0
  167. package/src/config/owner-registry.ts +22 -17
  168. package/src/config/provider-validation.ts +45 -2
  169. package/src/config/schema/config-schema.ts +22 -0
  170. package/src/config/schema/leaf-validators.ts +38 -9
  171. package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
  172. package/src/config/schema/native-reasoning-retention.ts +26 -0
  173. package/src/config.ts +2 -0
  174. package/src/generated/compatibility-version.json +608 -284
  175. package/src/grok/sync.ts +1 -1
  176. package/src/images/loop.ts +46 -8
  177. package/src/integrations/catalog-refresh.ts +1 -1
  178. package/src/integrations/mutation-plan.ts +1 -0
  179. package/src/integrations/registry.ts +8 -0
  180. package/src/lib/codex-restart-contract.ts +15 -0
  181. package/src/lib/desktop-upstream-tunnel.ts +18 -53
  182. package/src/lib/empty-tool-catalog.ts +62 -0
  183. package/src/lib/errors.ts +19 -3
  184. package/src/lib/gcp-adc.ts +96 -9
  185. package/src/lib/gcp-credential-input.ts +12 -0
  186. package/src/lib/lab-live-route-production.ts +5 -0
  187. package/src/lib/opaque-proxy-tunnel.ts +217 -0
  188. package/src/lib/redact.ts +2 -2
  189. package/src/oauth/account-quota-rank.ts +69 -2
  190. package/src/oauth/anthropic-account-refusal.ts +20 -2
  191. package/src/oauth/anthropic-oauth-definitions.ts +3 -1
  192. package/src/oauth/anthropic-routing.ts +1 -21
  193. package/src/oauth/anthropic.ts +38 -7
  194. package/src/oauth/callback-server.ts +27 -1
  195. package/src/oauth/chatgpt-device.ts +1 -1
  196. package/src/oauth/chatgpt-shared.ts +83 -0
  197. package/src/oauth/chatgpt.ts +8 -72
  198. package/src/oauth/dsh.ts +203 -0
  199. package/src/oauth/generic-account-failover.ts +33 -7
  200. package/src/oauth/generic-session-affinity.ts +125 -0
  201. package/src/oauth/index.ts +184 -109
  202. package/src/oauth/local-token-detect.ts +21 -1
  203. package/src/oauth/login-cli-errors.ts +44 -0
  204. package/src/oauth/login-cli.ts +69 -7
  205. package/src/oauth/login-flow-state.ts +371 -23
  206. package/src/oauth/meta-muse-errors.ts +41 -0
  207. package/src/oauth/meta-muse-keychain.ts +52 -0
  208. package/src/oauth/meta-muse.ts +51 -84
  209. package/src/oauth/model-discovery-auth.ts +3 -0
  210. package/src/oauth/pool-settings-capability.ts +4 -2
  211. package/src/oauth/refresh-flight-retirement.ts +48 -0
  212. package/src/oauth/session-affinity-key.ts +23 -0
  213. package/src/oauth/store-anthropic-instance.ts +9 -2
  214. package/src/oauth/store.ts +12 -5
  215. package/src/protocols/encoders/adapter-events.ts +5 -4
  216. package/src/protocols/encoders/messages.ts +9 -6
  217. package/src/protocols/provider-summary.ts +2 -1
  218. package/src/providers/api-key-cooldowns.ts +49 -0
  219. package/src/providers/api-key-resolve.ts +11 -6
  220. package/src/providers/api-key-response-ownership.ts +57 -0
  221. package/src/providers/api-key-selection.ts +31 -6
  222. package/src/providers/api-keys.ts +214 -3
  223. package/src/providers/claude-cli-usage.ts +96 -0
  224. package/src/providers/dsh-account-destination.ts +31 -0
  225. package/src/providers/dsh-account-models.ts +41 -0
  226. package/src/providers/gcp-credential-cleanup.ts +59 -0
  227. package/src/providers/key-failover.ts +54 -50
  228. package/src/providers/key-store.ts +7 -2
  229. package/src/providers/model-rename-fields.ts +3 -0
  230. package/src/providers/model-rename-migration.ts +2 -2
  231. package/src/providers/openai-tiers-destination.ts +11 -2
  232. package/src/providers/openai-tiers.ts +5 -12
  233. package/src/providers/opencode-go-transport.ts +3 -1
  234. package/src/providers/quota/account-cache.ts +6 -5
  235. package/src/providers/quota/account-row.ts +34 -0
  236. package/src/providers/quota/antigravity.ts +28 -7
  237. package/src/providers/quota/dsh-account.ts +70 -0
  238. package/src/providers/quota/vendor-probes-oauth.ts +1 -93
  239. package/src/providers/quota-types.ts +2 -0
  240. package/src/providers/quota.ts +14 -20
  241. package/src/providers/registry/entries-core.ts +37 -8
  242. package/src/providers/registry/types.ts +1 -1
  243. package/src/providers/request-pacing.ts +77 -19
  244. package/src/providers/routed-config.ts +339 -0
  245. package/src/providers/service-tier.ts +2 -1
  246. package/src/quota/window-mapping.ts +5 -2
  247. package/src/responses/anthropic-progress.ts +34 -0
  248. package/src/responses/assistant-replay.ts +35 -0
  249. package/src/responses/compaction-history-framing.ts +65 -0
  250. package/src/responses/compaction.ts +6 -1
  251. package/src/responses/incomplete-reason.ts +31 -0
  252. package/src/responses/parser.ts +50 -5
  253. package/src/responses/reasoning-envelope.ts +4 -0
  254. package/src/responses/reasoning-replay-cache.ts +24 -2
  255. package/src/responses/reasoning-retention.ts +173 -0
  256. package/src/responses/retained-compaction.ts +46 -0
  257. package/src/responses/schema.ts +23 -0
  258. package/src/router.ts +8 -321
  259. package/src/routing/health.ts +37 -22
  260. package/src/routing/history/indexer.ts +6 -5
  261. package/src/routing/history/ingest.ts +6 -2
  262. package/src/routing/profile.ts +1 -1
  263. package/src/server/admission-model-scope.ts +17 -0
  264. package/src/server/auth-cors.ts +11 -0
  265. package/src/server/chat-completions.ts +7 -1
  266. package/src/server/chat-native-reasoning.ts +123 -0
  267. package/src/server/chat-native-sse.ts +110 -7
  268. package/src/server/chat-native.ts +76 -17
  269. package/src/server/claude-messages.ts +55 -414
  270. package/src/server/effort-policy.ts +1 -1
  271. package/src/server/images.ts +15 -2
  272. package/src/server/index/model-alias-id.ts +36 -0
  273. package/src/server/index/optional-listeners.ts +2 -0
  274. package/src/server/index/serve-options.ts +9 -1
  275. package/src/server/index/startup-warnings.ts +2 -0
  276. package/src/server/index.ts +1 -1
  277. package/src/server/inference/advisor-diagnostics.ts +31 -0
  278. package/src/server/management/advisor-routes.ts +15 -4
  279. package/src/server/management/claude-desktop-picker-routes.ts +1 -1
  280. package/src/server/management/combo-routes.ts +176 -50
  281. package/src/server/management/companion-routes.ts +1 -1
  282. package/src/server/management/config-routes.ts +75 -4
  283. package/src/server/management/cursor-integration-routes.ts +1 -1
  284. package/src/server/management/model-routes.ts +11 -6
  285. package/src/server/management/model-rows.ts +14 -0
  286. package/src/server/management/native-integration-routes.ts +2 -7
  287. package/src/server/management/native-reasoning-retention-routes.ts +41 -0
  288. package/src/server/management/oauth-account-routes.ts +87 -14
  289. package/src/server/management/provider-gcp-credential-routes.ts +29 -0
  290. package/src/server/management/provider-key-pacing-routes.ts +31 -0
  291. package/src/server/management/provider-overwrite-carry.ts +7 -0
  292. package/src/server/management/provider-routes.ts +25 -30
  293. package/src/server/management/route-registry.ts +10 -1
  294. package/src/server/management-api.ts +3 -1
  295. package/src/server/messages-native-tool-guard.ts +185 -0
  296. package/src/server/messages-native.ts +101 -21
  297. package/src/server/messages-shared.ts +431 -0
  298. package/src/server/relay.ts +1 -1
  299. package/src/server/request-log-terminal-status.ts +1 -1
  300. package/src/server/request-log.ts +6 -1
  301. package/src/server/responses/account-change-state.ts +8 -2
  302. package/src/server/responses/adapter-continuation.ts +13 -1
  303. package/src/server/responses/adapter-delivery.ts +19 -6
  304. package/src/server/responses/adapter-dispatch.ts +19 -2
  305. package/src/server/responses/advisor-retention.ts +117 -0
  306. package/src/server/responses/advisor-slot.ts +73 -41
  307. package/src/server/responses/codex-ws-wire.ts +15 -8
  308. package/src/server/responses/compact.ts +303 -125
  309. package/src/server/responses/compaction-recovery.ts +18 -10
  310. package/src/server/responses/compaction-retention.ts +61 -0
  311. package/src/server/responses/context-overflow.ts +11 -6
  312. package/src/server/responses/core-auth.ts +6 -0
  313. package/src/server/responses/core-codex-account.ts +11 -1
  314. package/src/server/responses/core-combo-failure.ts +2 -0
  315. package/src/server/responses/core-combo.ts +120 -36
  316. package/src/server/responses/core-errors.ts +3 -3
  317. package/src/server/responses/core-lifetime.ts +5 -2
  318. package/src/server/responses/core-opaque-recovery.ts +35 -0
  319. package/src/server/responses/core-options.ts +5 -0
  320. package/src/server/responses/core-pipeline.ts +141 -0
  321. package/src/server/responses/core-replay.ts +18 -2
  322. package/src/server/responses/core.ts +2 -115
  323. package/src/server/responses/fetch-helpers.ts +73 -19
  324. package/src/server/responses/main-hard-lock-routing.ts +109 -0
  325. package/src/server/responses/native-response-control.ts +4 -2
  326. package/src/server/responses/passthrough-delivery.ts +3 -1
  327. package/src/server/responses/passthrough-dispatch.ts +15 -13
  328. package/src/server/responses/request-prepare.ts +76 -12
  329. package/src/server/responses/request-sidecar-auth.ts +2 -2
  330. package/src/server/responses/request-transport.ts +104 -27
  331. package/src/server/responses/response-effects.ts +1 -0
  332. package/src/server/responses/run-turn-execution.ts +96 -30
  333. package/src/server/responses/sidecar-execution.ts +20 -2
  334. package/src/server/responses/terminal-guard.ts +2 -16
  335. package/src/service/auth-preflight.ts +71 -0
  336. package/src/service/cli.ts +4 -2
  337. package/src/service/definition.ts +97 -0
  338. package/src/service/guards.ts +1 -61
  339. package/src/service/health-sentinel-linux.ts +112 -0
  340. package/src/service/health-sentinel-main.ts +76 -57
  341. package/src/service/health.ts +5 -89
  342. package/src/service/launchd.ts +27 -7
  343. package/src/service/logging.ts +72 -9
  344. package/src/service/repair.ts +2 -1
  345. package/src/service/state.ts +7 -8
  346. package/src/service/systemd.ts +1 -1
  347. package/src/service/windows-guarded-manager.ts +2 -1
  348. package/src/service/windows-ops.ts +1 -1
  349. package/src/service/windows-scheduler.ts +2 -7
  350. package/src/service/windows-taskxml.ts +2 -2
  351. package/src/service/windows-wscript.ts +14 -0
  352. package/src/service.ts +4 -2
  353. package/src/sidecar/auth.ts +2 -0
  354. package/src/types/config.ts +64 -3
  355. package/src/types/opencode-go.ts +61 -0
  356. package/src/types/opencode-zen.ts +29 -0
  357. package/src/types/provider.ts +32 -6
  358. package/src/types/request.ts +23 -2
  359. package/src/types/wire.ts +29 -37
  360. package/src/types.ts +1 -0
  361. package/src/update/npm-invocation.mjs +12 -2
  362. package/src/usage/cost.ts +48 -12
  363. package/src/usage/expected-prices.ts +3 -0
  364. package/src/usage/ledger-retention.ts +2 -1
  365. package/src/usage/ledger-scanner.ts +6 -5
  366. package/src/usage/log.ts +17 -8
  367. package/src/usage/summary.ts +22 -9
  368. package/src/usage/user-cost-overlays.ts +14 -19
  369. package/src/usage/user-cost-validation.ts +38 -0
  370. package/src/vision/eligibility.ts +13 -0
  371. package/src/web-search/loop.ts +55 -5
  372. package/src/web-search/progress-stream.ts +4 -1
  373. package/src/web-search/run-turn-loop.ts +9 -1
  374. package/gui/dist/assets/App-BjlttVLy.js +0 -58
  375. package/gui/dist/assets/App-CSJvmjNs.css +0 -1
  376. package/gui/dist/assets/index-BB6uK6tU.css +0 -1
  377. package/gui/dist/assets/index-BMssP4k3.js +0 -94
  378. package/gui/dist/assets/quota-summary-D8M1bc_R.js +0 -1
@@ -214,6 +214,13 @@ export function applyProviderCompatPatchFields(
214
214
  else return { error: "preserveResponsesReasoningContent must be a boolean" };
215
215
  touched = true;
216
216
  }
217
+ if (Object.hasOwn(rawBody, "inferContextWindow")) {
218
+ const value = rawBody.inferContextWindow;
219
+ if (value === null) delete next.inferContextWindow;
220
+ else if (typeof value === "boolean") next.inferContextWindow = value;
221
+ else return { error: "inferContextWindow must be a boolean or null" };
222
+ touched = true;
223
+ }
217
224
  if (Object.hasOwn(rawBody, "reasoningWireFormat")) {
218
225
  const value = rawBody.reasoningWireFormat;
219
226
  if (value === null) {
@@ -1,6 +1,8 @@
1
1
  import { contextTierRecordConfigError, modelCapabilitiesConfigError, mergeModelCapabilities } from "../../config/provider-validation";
2
2
  import { DECLARABLE_HOSTED_TOOL_TYPES } from "../../responses/hosted-tool-policy";
3
3
  import { randomUUID } from "node:crypto";
4
+ import { cleanupRemovedGcpCredentials, providerGcpCredentialMarkers, snapshotProviderGcpCredentialMarkers } from "../../providers/gcp-credential-cleanup";
5
+ import { providerGcpCredentialInputResponse, removeProviderWithGcpCredentials } from "./provider-gcp-credential-routes";
4
6
  import { readFileSync } from "node:fs";
5
7
  import { isDeepStrictEqual } from "node:util";
6
8
  import type { CatalogModel } from "../../codex/catalog";
@@ -78,7 +80,7 @@ import { cachedProviderQuotaIsExhausted } from "../../combos/resolve";
78
80
  import { probeJevDecisionProvider } from "../../combos/jev";
79
81
  import { comboDependsOnProviderRoute } from "./decision-model-validation";
80
82
  import { clearKeyCooldowns, forgetApiKeyRotationCursor } from "../../providers/key-failover";
81
- import { providerRequestPacingStatus } from "../../providers/request-pacing";
83
+ import { handleProviderKeyPacingRoutes } from "./provider-key-pacing-routes";
82
84
  import { CODEX_FORWARD_BASE_URL, isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
83
85
  import { codexAccountNamespaceProviderCollisionError } from "../../codex/account-namespace-match";
84
86
  import { clearThreadAccountMap } from "../../codex/routing";
@@ -853,7 +855,7 @@ function providerRoutingQuota(config: OcxConfig, name: string, now: number): Pro
853
855
 
854
856
  // Removing search/MCP windows may leave only a timestamp. That is not inference evidence.
855
857
  const percentages = [quota.fiveHourPercent, quota.weeklyPercent, quota.monthlyPercent,
856
- ...(quota.customWindows ?? []).map(window => window.percent)];
858
+ ...(quota.customWindows ?? []).filter(window => window.modelId === undefined).map(window => window.percent)];
857
859
  const hasPercentage = percentages.some(value => typeof value === "number" && Number.isFinite(value) && value >= 0);
858
860
  const credits = quota.creditsUsd;
859
861
  const hasCredits = credits !== undefined && Number.isFinite(credits.percent)
@@ -864,7 +866,7 @@ function providerRoutingQuota(config: OcxConfig, name: string, now: number): Pro
864
866
  let validUntil = quota.updatedAt + PROVIDER_QUOTA_MAX_AGE_MS;
865
867
  if (state === "exhausted") {
866
868
  const resets = [quota.fiveHourResetAt, quota.weeklyResetAt, quota.monthlyResetAt,
867
- ...(quota.customWindows ?? []).map(window => window.resetAt)]
869
+ ...(quota.customWindows ?? []).filter(window => window.modelId === undefined).map(window => window.resetAt)]
868
870
  .filter((reset): reset is number => typeof reset === "number" && Number.isFinite(reset)
869
871
  && reset > now && reset < validUntil)
870
872
  .sort((left, right) => left - right);
@@ -895,21 +897,8 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
895
897
  });
896
898
  }
897
899
 
898
- if (url.pathname === "/api/provider-request-pacing" && req.method === "GET") {
899
- const name = url.searchParams.get("name")?.trim();
900
- if (name) {
901
- if (!isValidProviderName(name) || !hasOwnProvider(config.providers, name)) {
902
- return jsonResponse({ error: "unknown provider" }, 404);
903
- }
904
- return jsonResponse(providerRequestPacingStatus(name, config.providers[name]!));
905
- }
906
- return jsonResponse(Object.fromEntries(
907
- Object.entries(config.providers).map(([providerName, provider]) => [
908
- providerName,
909
- providerRequestPacingStatus(providerName, provider),
910
- ]),
911
- ));
912
- }
900
+ const keyPacingResponse = handleProviderKeyPacingRoutes(ctx);
901
+ if (keyPacingResponse) return keyPacingResponse;
913
902
 
914
903
  if (url.pathname === "/api/providers" && req.method === "GET") {
915
904
  return jsonResponse(Object.entries(config.providers).map(([name, p]) => ({
@@ -922,6 +911,7 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
922
911
  requestPacing: p.requestPacing,
923
912
  models: p.models ?? [],
924
913
  contextWindow: p.contextWindow,
914
+ inferContextWindow: p.inferContextWindow,
925
915
  modelContextWindows: p.modelContextWindows,
926
916
  modelContextTiers: p.modelContextTiers,
927
917
  modelCapabilities: p.modelCapabilities,
@@ -1073,6 +1063,7 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
1073
1063
  if (resolvedError) return jsonResponse({ error: resolvedError, code: "invalid_provider_destination" }, 400);
1074
1064
  }
1075
1065
 
1066
+ let removedCredentials: string[] = [];
1076
1067
  const outcome = mutatePersistedConfig<ProviderEditorMutationValue>(persisted => {
1077
1068
  if (!isDeepStrictEqual(providerEditorConfigDTO(persisted), baselineResult.value)) {
1078
1069
  return {
@@ -1095,6 +1086,7 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
1095
1086
  initializeProviderModelSelection(name, provider, undefined, candidate.config);
1096
1087
  }
1097
1088
  }
1089
+ removedCredentials = Object.values(persisted.providers).flatMap(providerGcpCredentialMarkers);
1098
1090
  persisted.defaultProvider = candidate.config.defaultProvider;
1099
1091
  persisted.providers = structuredClone(candidate.config.providers);
1100
1092
  persisted.disabledModels = candidate.config.disabledModels;
@@ -1120,6 +1112,7 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
1120
1112
  return jsonResponse({ error: outcome.value.error, code: outcome.value.code }, outcome.value.status);
1121
1113
  }
1122
1114
 
1115
+ cleanupRemovedGcpCredentials(removedCredentials);
1123
1116
  adoptProviderEditorCandidate(config, outcome.value.config);
1124
1117
  reconcileLiveStateStores();
1125
1118
  refreshConfigDerivedRegistries(outcome.value.config);
@@ -1137,16 +1130,15 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
1137
1130
  return jsonResponse({ success: true, catalogRefresh });
1138
1131
  }
1139
1132
 
1140
- // Add (or overwrite) a single provider. Merges into the live in-memory config and
1141
- // persists — existing providers' real keys are never round-tripped (unlike PUT /api/config,
1142
- // which would re-save the masked keys from GET). Live routing picks it up immediately.
1133
+ // Provider POST merges directly; existing secrets never round-trip through masked DTOs.
1143
1134
  if (url.pathname === "/api/providers" && req.method === "POST") {
1144
1135
  let body: { name?: unknown; provider?: unknown; setDefault?: boolean };
1145
1136
  try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
1146
1137
  const name = typeof body.name === "string" ? body.name.trim() : "";
1147
1138
  if (!isPlainRecord(body.provider)) return jsonResponse({ error: "provider must be a plain object" }, 400);
1148
- // Same prohibition as PATCH: the canonical OpenAI row never carries these fields, and a clear
1149
- // form would otherwise be normalized away before the merged-row guard could see it.
1139
+ const credentialResponse = providerGcpCredentialInputResponse(body.provider);
1140
+ if (credentialResponse) return credentialResponse;
1141
+ // Reject canonical-row clears before normalization hides their ownership.
1150
1142
  if (name === "openai" && (Object.hasOwn(body.provider, "autoReviewModel") || Object.hasOwn(body.provider, "autoReviewModelOverrides"))) {
1151
1143
  return jsonResponse({ error: "provider openai must not include autoReviewModel or autoReviewModelOverrides" }, 400);
1152
1144
  }
@@ -1357,7 +1349,7 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
1357
1349
  const candidate = stripRegistryOnlyStaticHeaders(name, prov);
1358
1350
  const previous = Object.getOwnPropertyDescriptor(config.providers, name);
1359
1351
  const rollback = pinsOwned ? captureConfigTopLevelRollback(config, ["defaultProvider", "modelDiscovery", "disabledModels"]) : undefined;
1360
- let validationError: string | undefined;
1352
+ let validationError: string | undefined, removedCredentials: string[] = [];
1361
1353
  withConfigMutationLockSync(() => {
1362
1354
  validationError = anthropicInstancePublicationError(name, candidate, config.providers[name]);
1363
1355
  if (validationError) return;
@@ -1365,6 +1357,12 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
1365
1357
  if (submittedModelContextTiers && liveTiers) candidate.modelContextTiers = Object.assign(Object.create(null), liveTiers, candidate.modelContextTiers ?? {});
1366
1358
  else if (!submittedModelContextTiers && liveTiers) candidate.modelContextTiers = { ...liveTiers };
1367
1359
  else if (!submittedModelContextTiers) delete candidate.modelContextTiers;
1360
+ // Full forms omit the opt-in; preserve the latest value, including a concurrent clear.
1361
+ if (!Object.hasOwn(rawProvider, "inferContextWindow")) {
1362
+ const liveInference = config.providers[name]?.inferContextWindow;
1363
+ if (liveInference === undefined) delete candidate.inferContextWindow;
1364
+ else candidate.inferContextWindow = liveInference;
1365
+ }
1368
1366
  const draft = { ...(registrationDraft ?? config), providers: { ...config.providers, [name]: candidate }, ...(body.setDefault === true ? { defaultProvider: name } : {}) };
1369
1367
  const validation = validateConfigCandidate(draft); if (!validation.ok) { validationError = validation.error; return; }
1370
1368
  try {
@@ -1375,6 +1373,7 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
1375
1373
  });
1376
1374
  }
1377
1375
  }
1376
+ removedCredentials = snapshotProviderGcpCredentialMarkers(name, config.providers[name]);
1378
1377
  config.providers[name] = candidate;
1379
1378
  if (body.setDefault === true) config.defaultProvider = name;
1380
1379
  (deps.saveConfigPreservingClaudeCode ?? save)(config);
@@ -1388,6 +1387,7 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
1388
1387
  }
1389
1388
  });
1390
1389
  if (validationError !== undefined) return jsonResponse({ error: validationError }, 400);
1390
+ cleanupRemovedGcpCredentials(removedCredentials);
1391
1391
  reconcileLiveStateStores();
1392
1392
  if (prov.apiKey && prov.apiKeyPool) {
1393
1393
  const { addProviderApiKey } = await import("../../providers/api-keys");
@@ -1848,12 +1848,7 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
1848
1848
  const { saveConfigPreservingClaudeCode: save } = await import("../../config");
1849
1849
  // Deleting still succeeds; the response names the shadow-call target left without a provider.
1850
1850
  const shadowDependency = shadowInterceptProviderDependency(config, name);
1851
- if (fallbackDefault) config.defaultProvider = fallbackDefault;
1852
- delete config.providers[name];
1853
- const { dropProviderCustomModels } = await import("../../providers/provider-id-rewrite");
1854
- const droppedCustomModels = dropProviderCustomModels(config, name);
1855
- forgetProviderContextCap(config, name);
1856
- save(config);
1851
+ const droppedCustomModels = removeProviderWithGcpCredentials(config, name, fallbackDefault, save);
1857
1852
  await replaceProviderAccountSet(name, null);
1858
1853
  reconcileLiveStateStores();
1859
1854
  const { clearModelCache: clearCache } = await import("../../codex/model-cache");
@@ -97,8 +97,12 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
97
97
  // server/management-api
98
98
  { method: "POST", path: "/api/stop", module: "server/management-api", mutates: true },
99
99
  // codex/auth-api
100
+ { method: "GET", path: "/api/codex-auth/preserve-signed-in", module: "codex/auth-api/routes", mutates: false },
101
+ { method: "PUT", path: "/api/codex-auth/preserve-signed-in", module: "codex/auth-api/routes", mutates: true },
100
102
  { method: "DELETE", path: "/api/codex-auth/accounts", module: "codex/auth-api/routes", mutates: true },
101
103
  { method: "GET", path: "/api/codex-auth/accounts", module: "codex/auth-api/routes", mutates: false },
104
+ { method: "GET", path: "/api/codex-auth/accounts/use-remaining", module: "codex/auth-api/routes", mutates: false },
105
+ { method: "PUT", path: "/api/codex-auth/accounts/use-remaining", module: "codex/auth-api/routes", mutates: true },
102
106
  { method: "GET", path: "/api/codex-auth/active", module: "codex/auth-api/routes", mutates: false },
103
107
  { method: "GET", path: "/api/codex-auth/login-status", module: "codex/auth-api/routes", mutates: false },
104
108
  { method: "GET", path: "/api/codex-auth/quota", module: "codex/auth-api/routes", mutates: false },
@@ -217,8 +221,11 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
217
221
  { method: "POST", path: "/api/combos/decision-test", module: "server/management/decision-routes", mutates: true },
218
222
  // server/management/config-routes
219
223
  { method: "GET", path: "/api/config", module: "server/management/config-routes", mutates: false },
224
+ { method: "GET", path: "/api/auto-review-settings", module: "server/management/config-routes", mutates: false },
220
225
  { method: "GET", path: "/api/diagnostics/project-config", module: "server/management/config-routes", mutates: false },
221
226
  { method: "GET", path: "/api/settings", module: "server/management/config-routes", mutates: false },
227
+ { method: "GET", path: "/api/native-reasoning-retention", module: "server/management/native-reasoning-retention-routes", mutates: false, exempt: { reason: "local-transport", why: "The CLI reads the same nativeReasoningRetention policy through ocx config get; no separate HTTP-specific settings verb is required." } },
228
+ { method: "PUT", path: "/api/native-reasoning-retention", module: "server/management/native-reasoning-retention-routes", mutates: true, exempt: { reason: "deferred-verb", why: "A dedicated live CLI verb for this policy is deferred to native-reasoning-retention-live-cli; ocx config set/unset currently edits the same block on disk without live convergence.", owner: "native-reasoning-retention-live-cli", ownerDoc: "structure/transports/native-reasoning-retention.md" } },
222
229
  { method: "GET", path: "/api/shadow-call-settings", module: "server/management/config-routes", mutates: false },
223
230
  { method: "GET", path: "/api/sidecar-settings", module: "server/management/config-routes", mutates: false },
224
231
  { method: "GET", path: "/api/startup-health", module: "server/management/config-routes", mutates: false },
@@ -231,6 +238,7 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
231
238
  { method: "POST", path: "/api/windows-tray", module: "server/management/config-routes", mutates: true },
232
239
  { method: "PUT", path: "/api/config", module: "server/management/config-routes", mutates: true, exempt: { reason: "disabled", why: "Returns 405 by design; provider changes go through POST /api/providers." } },
233
240
  { method: "PUT", path: "/api/settings", module: "server/management/config-routes", mutates: true },
241
+ { method: "PUT", path: "/api/auto-review-settings", module: "server/management/config-routes", mutates: true },
234
242
  { method: "PUT", path: "/api/shadow-call-settings", module: "server/management/config-routes", mutates: true },
235
243
  { method: "PUT", path: "/api/sidecar-settings", module: "server/management/config-routes", mutates: true },
236
244
  // server/management/integration-routes
@@ -277,6 +285,7 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
277
285
  { method: "GET", path: "/api/usage/timeline", module: "server/management/usage-timeline-routes", mutates: false },
278
286
  // server/management/advisor-routes
279
287
  { method: "GET", path: "/api/advisor/settings", module: "server/management/advisor-routes", mutates: false },
288
+ { method: "GET", path: "/api/advisor/activity", module: "server/management/advisor-routes", mutates: false },
280
289
  { method: "PUT", path: "/api/advisor/settings", module: "server/management/advisor-routes", mutates: true },
281
290
  // server/management/companion-routes
282
291
  { method: "POST", path: "/api/companion/open-in-browser", module: "server/management/companion-routes", mutates: true, exempt: { reason: "browser-navigation", why: "Opens the companion's current view in the system browser under normal management admission; the CLI reads the underlying settings and usage directly." } },
@@ -360,7 +369,7 @@ export const MANAGEMENT_ROUTES: readonly ManagementRoute[] = [
360
369
  { method: "GET", path: "/api/provider-context-caps", module: "server/management/provider-routes", mutates: false },
361
370
  { method: "GET", path: "/api/provider-presets", module: "server/management/provider-routes", mutates: false },
362
371
  { method: "GET", path: "/api/provider-quotas", module: "server/management/provider-routes", mutates: false },
363
- { method: "GET", path: "/api/provider-request-pacing", module: "server/management/provider-routes", mutates: false },
372
+ { method: "GET", path: "/api/provider-request-pacing", module: "server/management/provider-key-pacing-routes", mutates: false },
364
373
  { method: "GET", path: "/api/providers", module: "server/management/provider-routes", mutates: false },
365
374
  { method: "PATCH", path: "/api/providers", module: "server/management/provider-routes", mutates: true },
366
375
  { method: "POST", path: "/api/providers", module: "server/management/provider-routes", mutates: true },
@@ -59,6 +59,7 @@ import { applySystemEnvToggle } from "./system-env";
59
59
 
60
60
  import type { ManagementApiDeps } from "./management/context";
61
61
  import { handleConfigRoutes } from "./management/config-routes";
62
+ import { handleNativeReasoningRetentionRoutes } from "./management/native-reasoning-retention-routes";
62
63
  import { handleLogsUsageRoutes } from "./management/logs-usage-routes";
63
64
  import { handleStorageLogGuardRoutes } from "./management/storage-log-guard-routes";
64
65
  import { handleRequestHistoryRoutes } from "./management/request-history-routes";
@@ -344,6 +345,7 @@ export async function handleManagementAPI(
344
345
  ?? (await handleLinkRoutesOnDemand(ctx))
345
346
  ?? (await handleRemoteWorkspaceRoutesOnDemand(ctx))
346
347
  ?? (await handleConfigRoutes(ctx))
348
+ ?? (await handleNativeReasoningRetentionRoutes(ctx))
347
349
  ?? (await handleStorageLogGuardRoutes(ctx))
348
350
  ?? (await handleLogsUsageRoutes(ctx))
349
351
  ?? (await handleRequestHistoryRoutes(ctx))
@@ -519,7 +521,7 @@ export async function handleManagementAPI(
519
521
  const { handleMainDeviceReauthAPI } = await import("../codex/main-device-reauth-api");
520
522
  return handleMainDeviceReauthAPI(req, url, config);
521
523
  }
522
- const { handleCodexAuthAPI } = await import("../codex/auth-api");
524
+ const { handleCodexAuthAPI } = await import("../codex/auth-api/routes");
523
525
  const { ConfigMutationLockError } = await import("../config");
524
526
  const { CodexCredentialRefreshLockTimeoutError } = await import("../codex/account-store");
525
527
  try {
@@ -0,0 +1,185 @@
1
+ import { anthropicErrorBody } from "../claude/outbound";
2
+ import { redactSecretString } from "../lib/redact";
3
+ import { sseFieldValue } from "../lib/sse-decoder";
4
+ import { TRANSLATOR_MAX_SSE_EVENT_BYTES, type TranslatorBudget } from "../lib/translator-budget";
5
+ import { UNDECLARED_TOOL_CALL_ERROR_CODE, undeclaredToolCallMessage } from "./responses-undeclared-tool-guard";
6
+ import { createSseBlockBuffer, sseDataPayload } from "./sse-payload-rewrite";
7
+
8
+ type Rec = Record<string, unknown>;
9
+ const isRec = (value: unknown): value is Rec => value !== null && typeof value === "object" && !Array.isArray(value);
10
+ const NO_DECLARED_TOOLS: ReadonlySet<string> = new Set();
11
+
12
+ /** Inline system-message definitions extend an otherwise empty native catalog. */
13
+ export function nativeMessagesInlineToolNames(body: Rec): ReadonlySet<string> {
14
+ const definitions = new Set<string>();
15
+ const active = new Set<string>();
16
+ if (!Array.isArray(body.messages)) return active;
17
+ for (const message of body.messages) {
18
+ if (!isRec(message) || message.role !== "system" || !Array.isArray(message.content)) continue;
19
+ for (const block of message.content) {
20
+ if (!isRec(block) || !isRec(block.tool)) continue;
21
+ const tool = block.tool;
22
+ if (block.type === "tool_addition" && tool.type === "tool_definition" && isRec(tool.definition)
23
+ && typeof tool.definition.name === "string" && tool.definition.name.length > 0) {
24
+ definitions.add(tool.definition.name);
25
+ active.add(tool.definition.name);
26
+ } else if (tool.type === "tool_reference" && typeof tool.name === "string") {
27
+ if (block.type === "tool_addition" && definitions.has(tool.name)) active.add(tool.name);
28
+ // Like native admission, only a permanent removal withdraws a declaration.
29
+ if (block.type === "tool_removal" && (message.clear_at === undefined || message.clear_at === "never")) active.delete(tool.name);
30
+ }
31
+ }
32
+ }
33
+ return active;
34
+ }
35
+
36
+ /** Inspect only native message content; opaque extension fields do not carry client calls. */
37
+ export function nativeMessagesClientToolName(value: unknown, declared: ReadonlySet<string> = NO_DECLARED_TOOLS): string | undefined {
38
+ if (!isRec(value)) return undefined;
39
+ const content = Array.isArray(value.content) ? value.content : [];
40
+ const block = content.find(item => isRec(item) && item.type === "tool_use"
41
+ && (typeof item.name !== "string" || !declared.has(item.name)));
42
+ if (isRec(block)) return typeof block.name === "string" ? block.name : "<unnamed>";
43
+ return undefined;
44
+ }
45
+
46
+ /** Used only when the current native request explicitly declares no client tools. */
47
+ export function guardNativeMessagesEmptyToolCatalog(
48
+ source: ReadableStream<Uint8Array>,
49
+ budget: TranslatorBudget,
50
+ onRefusal: (message: string) => void,
51
+ declared: ReadonlySet<string> = NO_DECLARED_TOOLS,
52
+ ): ReadableStream<Uint8Array> {
53
+ const reader = source.getReader();
54
+ const decoder = new TextDecoder();
55
+ const encoder = new TextEncoder();
56
+ const scope = { kind: "live_transient" as const };
57
+ const buffer = createSseBlockBuffer(budget, bytes => {
58
+ if (bytes > TRANSLATOR_MAX_SSE_EVENT_BYTES) throw new Error("upstream SSE event exceeded the safe limit");
59
+ });
60
+ // Hosted tools share input_json_delta with client tools, but cannot grant client authority.
61
+ const permittedInputBlocks = new Set<number>();
62
+ let queuedBytes = 0;
63
+ let settled = false;
64
+ const releaseQueued = () => {
65
+ budget.releaseRetained(queuedBytes, scope);
66
+ queuedBytes = 0;
67
+ };
68
+ const clear = () => {
69
+ buffer.clear();
70
+ budget.releaseRetained(permittedInputBlocks.size * 32, scope);
71
+ permittedInputBlocks.clear();
72
+ };
73
+ const enqueue = (controller: ReadableStreamDefaultController<Uint8Array>, text: string) => {
74
+ const bytes = Buffer.byteLength(text);
75
+ const reservation = budget.reserveTransient(bytes, scope);
76
+ try {
77
+ controller.enqueue(encoder.encode(text));
78
+ reservation.commitRetained();
79
+ queuedBytes += bytes;
80
+ } catch (error) {
81
+ reservation.release();
82
+ throw error;
83
+ }
84
+ };
85
+ const cancelReader = (reason?: unknown) => {
86
+ void reader.cancel(reason).catch(() => {});
87
+ };
88
+ const process = (controller: ReadableStreamDefaultController<Uint8Array>, block: string, delimiter: string) => {
89
+ const payload = sseDataPayload(block);
90
+ let data: unknown;
91
+ let terminal = false;
92
+ try { data = payload === null ? undefined : JSON.parse(payload); } catch {
93
+ // The buffered Messages collector concatenates data lines without separators. Inspect
94
+ // that representation too, so an invalid SSE split cannot become a valid folded tool call.
95
+ try { data = payload === null ? undefined : JSON.parse(payload.replace(/\n/g, "")); } catch { /* preserve opaque frames */ }
96
+ }
97
+ if (isRec(data)) {
98
+ const eventName = block.split(/\r\n|\r|\n/).map(line => sseFieldValue(line, "event"))
99
+ .filter(value => value !== null).at(-1)?.trim();
100
+ // Stream clients use the payload type; the buffered collector uses the event field.
101
+ const isEvent = (type: string) => data.type === type || eventName === type;
102
+ terminal = isEvent("message_stop") || isEvent("error");
103
+ let name = isEvent("message_start") ? nativeMessagesClientToolName(data.message, declared) : undefined;
104
+ if (isEvent("content_block_start") && isRec(data.content_block) && data.content_block.type === "tool_use"
105
+ && (typeof data.content_block.name !== "string" || !declared.has(data.content_block.name))) {
106
+ name ??= typeof data.content_block.name === "string" ? data.content_block.name : "<unnamed>";
107
+ }
108
+ const index = typeof data.index === "number" && Number.isSafeInteger(data.index) ? data.index : undefined;
109
+ if (isEvent("content_block_delta") && isRec(data.delta) && data.delta.type === "input_json_delta"
110
+ && (index === undefined || !permittedInputBlocks.has(index))) name ??= "<unnamed>";
111
+ if (name !== undefined) {
112
+ clear();
113
+ const message = redactSecretString(undeclaredToolCallMessage(name));
114
+ onRefusal(message);
115
+ enqueue(controller, `event: error\ndata: ${JSON.stringify(anthropicErrorBody(502, message, "api_error", UNDECLARED_TOOL_CALL_ERROR_CODE))}\n\n`);
116
+ settled = true;
117
+ cancelReader(new Error(message));
118
+ controller.close();
119
+ return;
120
+ }
121
+ if (isEvent("content_block_start") && index !== undefined && isRec(data.content_block)) {
122
+ if (permittedInputBlocks.delete(index)) budget.releaseRetained(32, scope);
123
+ if (data.content_block.type === "server_tool_use" || data.content_block.type === "mcp_tool_use"
124
+ || data.content_block.type === "tool_use" && typeof data.content_block.name === "string" && declared.has(data.content_block.name)) {
125
+ budget.chargeRetained(32, scope);
126
+ permittedInputBlocks.add(index);
127
+ }
128
+ } else if (isEvent("content_block_stop") && index !== undefined && permittedInputBlocks.delete(index)) {
129
+ budget.releaseRetained(32, scope);
130
+ }
131
+ }
132
+ enqueue(controller, block + delimiter);
133
+ if (terminal) {
134
+ settled = true;
135
+ clear();
136
+ cancelReader();
137
+ controller.close();
138
+ }
139
+ };
140
+ return new ReadableStream<Uint8Array>({
141
+ async pull(controller) {
142
+ releaseQueued();
143
+ if (settled) return;
144
+ try {
145
+ for (;;) {
146
+ const block = buffer.next();
147
+ if (block) {
148
+ buffer.compact();
149
+ process(controller, block.block, block.delimiter);
150
+ return;
151
+ }
152
+ const { done, value } = await reader.read();
153
+ if (settled) return;
154
+ if (!done) {
155
+ buffer.append(decoder.decode(value, { stream: true }));
156
+ continue;
157
+ }
158
+ buffer.append(decoder.decode());
159
+ const tail = buffer.tail();
160
+ if (tail) process(controller, tail, "");
161
+ if (!settled) {
162
+ settled = true;
163
+ clear();
164
+ controller.close();
165
+ }
166
+ return;
167
+ }
168
+ } catch (error) {
169
+ if (settled) return;
170
+ settled = true;
171
+ clear();
172
+ cancelReader(error);
173
+ controller.error(error);
174
+ } finally {
175
+ if (settled) releaseQueued();
176
+ }
177
+ },
178
+ cancel(reason) {
179
+ settled = true;
180
+ clear();
181
+ releaseQueued();
182
+ return reader.cancel(reason);
183
+ },
184
+ });
185
+ }