@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
@@ -6,6 +6,9 @@ import { cloneProviderOpaqueToolCallMetadata } from "../responses/provider-opaqu
6
6
  import type { AttemptRecoveryKind } from "../usage/log";
7
7
  import { isTruncatedStopReason } from "../responses/truncated-stop-reason";
8
8
  import { bridgeToResponsesSSE } from "../bridge";
9
+ import { isTerminalRefusalIncomplete } from "../bridge/terminal-refusal";
10
+ import { guardEmptyToolCatalogStream, type ClientToolCatalogRestriction } from "../lib/empty-tool-catalog";
11
+ import { mergeUsage } from "../server/responses/empty-completion-guard";
9
12
  import { runWebSearch, type SidecarOutcome, type SidecarOutcomeRecorder, type SidecarSettings } from "./executor";
10
13
  import { runAnthropicWebSearch } from "./anthropic-executor";
11
14
  import { runXaiWebSearch, type XaiSearchOptions } from "./xai-executor";
@@ -143,6 +146,18 @@ async function* replay(events: AdapterEvent[]): AsyncGenerator<AdapterEvent> {
143
146
  for (const e of events) yield e;
144
147
  }
145
148
 
149
+ function terminalUsage(events: AdapterEvent[]): OcxUsage | undefined {
150
+ for (const e of events) if (e.type === "done" || e.type === "incomplete") return e.usage;
151
+ return undefined;
152
+ }
153
+
154
+ /** Hidden iterations add counters only: their checkpoint and raw wire usage are not the final terminal's. */
155
+ function countersOnly(usage: OcxUsage | undefined): OcxUsage | undefined {
156
+ if (!usage) return undefined;
157
+ const { contextTotalTokens: _context, rawUsage: _raw, ...counters } = usage;
158
+ return counters;
159
+ }
160
+
146
161
  /**
147
162
  * Collect the thinking block that preceded a web_search call in this iteration's events, so the
148
163
  * replayed assistant turn can carry it. Anthropic extended thinking REQUIRES the assistant
@@ -189,7 +204,11 @@ function extractIterationThinking(events: AdapterEvent[]): OcxThinkingContent[]
189
204
  };
190
205
 
191
206
  for (const e of events) {
192
- if (e.type === "thinking_delta") {
207
+ if (e.type === "text_delta" && e.anthropicThinkingSignature) {
208
+ flushVisible();
209
+ flushRaw();
210
+ parts.push({ type: "thinking", thinking: e.text, signature: e.anthropicThinkingSignature });
211
+ } else if (e.type === "thinking_delta") {
193
212
  flushRaw();
194
213
  thinking += e.thinking;
195
214
  } else if (e.type === "reasoning_raw_delta") {
@@ -272,6 +291,7 @@ class LoopError extends Error {
272
291
  */
273
292
  export interface WebSearchLoopDeps {
274
293
  parsed: OcxParsedRequest;
294
+ clientToolCatalogRestriction?: ClientToolCatalogRestriction;
275
295
  adapter: ProviderAdapter;
276
296
  incomingMeta: IncomingMeta;
277
297
  /**
@@ -297,6 +317,8 @@ export interface WebSearchLoopDeps {
297
317
  settings: SidecarSettings;
298
318
  maxSearches: number;
299
319
  forceEmptyResponseId?: boolean;
320
+ /** Responses inbound only: deliver a non-retryable upstream refusal as `response.failed`. */
321
+ terminalRefusalAsFailed?: boolean;
300
322
  abortSignal?: AbortSignal;
301
323
  recordSidecarOutcome?: SidecarOutcomeRecorder;
302
324
  beforeSidecarDispatch?: () => void;
@@ -320,6 +342,12 @@ export interface WebSearchLoopDeps {
320
342
  onFirstOutput?: () => void;
321
343
  /** Raw adapter usage at the terminal event, pre wire-normalization (see bridgeToResponsesSSE onUsage). */
322
344
  onUsage?: (usage: OcxUsage | undefined) => void;
345
+ /**
346
+ * After an `on429` rotation, offered the usage of the hidden iterations the left credential
347
+ * served. Returning true means it was billed to that credential's attempt, so `onUsage` will not
348
+ * report it again.
349
+ */
350
+ onRotatedUsage?: (usage: OcxUsage) => boolean;
323
351
  /** Observe the exact adapter request selected for each routed-model iteration. */
324
352
  onRequestBuilt?: (request: AdapterRequest) => void;
325
353
  /** Request-scoped executor retains the core's selection binding across loop retries. */
@@ -407,6 +435,12 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
407
435
  // One iteration beyond the forced answer is reserved for its empty-answer recovery below.
408
436
  const HARD_CAP = maxSearches + 3;
409
437
  let emptyAnswerRetries = 0;
438
+ // Every iteration re-sends the full context, but only the last one's terminal reaches the bridge.
439
+ // Search-calling passes and the discarded empty forced answer are summed here and added to what
440
+ // onUsage reports, so the request log meters what was actually sent. The client-facing terminal
441
+ // keeps the last iteration's usage: Codex and Claude Code size the context window from it, and
442
+ // a sum would read as a context several times larger and trigger early auto-compaction.
443
+ let hiddenUsage: OcxUsage | undefined;
410
444
  let accountRefusalOutputStarted = false;
411
445
  const connectTimeoutMs = deps.connectTimeoutMs ?? 200_000;
412
446
  const routedModelStallTimeoutMs = deps.routedModelStallTimeoutMs ?? 200_000;
@@ -586,6 +620,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
586
620
  const rotated = await deps.on429(prepared.response.headers.get("retry-after"), prepared.response.headers,
587
621
  iterParsed, prepared.response);
588
622
  if (!rotated) break;
623
+ if (hiddenUsage && deps.onRotatedUsage?.(hiddenUsage)) hiddenUsage = undefined;
589
624
  const retainRefusal = prepared.response.status === 403
590
625
  && deps.incomingMeta?.providerName === "google-antigravity" && rotated.recoveryKind === "oauth-account-403";
591
626
  // Keep the bounded refusal readable until the sibling actually returns headers.
@@ -681,6 +716,8 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
681
716
  signal,
682
717
  inactivityTimeoutMs: routedModelStallTimeoutMs,
683
718
  translatorBudget,
719
+ // A pass that fails was still billed; the error path reports it with the hidden passes.
720
+ onErrorUsage: usage => { hiddenUsage = mergeUsage(hiddenUsage, usage); },
684
721
  })) {
685
722
  if (event.type === "heartbeat") yield event;
686
723
  // Kiro's explicit-completion protocol marks ordinary assistant text as commentary while
@@ -689,7 +726,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
689
726
  // Codex show only `Working` until both Kiro attempts had finished (often 30-40 seconds).
690
727
  // Tool events remain buffered below, so the decision to invoke the hosted sidecar is still
691
728
  // atomic and no search call can escape before its stream has validated successfully.
692
- else if (event.type === "text_delta" && event.phase === "commentary") { accountRefusalOutputStarted = true; yield event; }
729
+ else if (event.type === "text_delta" && event.phase === "commentary" && !event.anthropicThinkingSignature) { accountRefusalOutputStarted = true; yield event; }
693
730
  else if (liveWindowOpen && LIVE_STREAMABLE.has(event.type)) {
694
731
  // Live events are ALSO buffered: the scanner still needs them for thinking extraction
695
732
  // and the forced-answer output check; only the terminal replay skips them (by count).
@@ -914,7 +951,10 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
914
951
  // limit-reached result instead of failing the turn (#6464: stripping
915
952
  // the declaration makes some models emit the raw call as visible
916
953
  // text).
917
- const shouldLoop = split.calls.length > 0 && !split.hasRealToolCall;
954
+ // A refused iteration ends the turn even when it carried a search call: running the
955
+ // search would send the refused context back to the model (#7148).
956
+ const shouldLoop = split.calls.length > 0 && !split.hasRealToolCall
957
+ && !split.passthrough.some(isTerminalRefusalIncomplete);
918
958
  if (!shouldLoop) {
919
959
  // #1001: a forced-answer pass that ends `done` must have produced
920
960
  // usable output — never a malformed tool call, and never silence.
@@ -944,6 +984,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
944
984
  stopReason: terminalEvent.stopReason,
945
985
  eventTypes: [...new Set(split.passthrough.map(event => event.type))],
946
986
  }));
987
+ hiddenUsage = mergeUsage(hiddenUsage, terminalEvent.usage);
947
988
  if (!split.hasMalformedToolCall && !split.hasRealToolCall && emptyAnswerRetries === 0) {
948
989
  emptyAnswerRetries++;
949
990
  console.warn("[web-search-loop] empty forced answer — retrying once without tools");
@@ -966,13 +1007,19 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
966
1007
  yield* replay(split.passthrough.slice(split.streamedPassthroughCount));
967
1008
  return;
968
1009
  }
1010
+ hiddenUsage = mergeUsage(hiddenUsage, terminalUsage(split.passthrough));
969
1011
  // The thinking that led to the search belongs to the FIRST call's assistant replay turn.
1012
+ for (const event of split.passthrough.slice(split.streamedPassthroughCount)) {
1013
+ if (event.type === "text_delta" && event.anthropicThinkingSignature) yield event;
1014
+ }
970
1015
  const iterationThinking = extractIterationThinking(split.passthrough);
971
1016
  for (const [callIndex, call] of split.calls.entries()) {
972
1017
  accountRefusalOutputStarted = true;
973
1018
  yield* runSearchCall(call, callIndex === 0 ? iterationThinking : []);
974
1019
  }
975
1020
  } catch (e) {
1021
+ // The bridge reports no usage for either error; the iterations already sent still count.
1022
+ if (hiddenUsage) deps.onUsage?.(hiddenUsage);
976
1023
  if (isTranslatorBudgetExceededError(e)) {
977
1024
  yield {
978
1025
  type: "error",
@@ -987,6 +1034,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
987
1034
  return;
988
1035
  }
989
1036
  }
1037
+ if (hiddenUsage) deps.onUsage?.(hiddenUsage);
990
1038
  yield { type: "error", message: "web-search loop exceeded its iteration cap" };
991
1039
  } finally {
992
1040
  if (abortSignal) abortSignal.removeEventListener("abort", linkAbort);
@@ -994,7 +1042,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
994
1042
  }
995
1043
 
996
1044
  const sse = bridgeToResponsesSSE(
997
- produce(), parsed._responseModelId ?? parsed.modelId, toolNsMap, freeform, toolSearch, () => {
1045
+ guardEmptyToolCatalogStream(produce(), deps.clientToolCatalogRestriction, translatorBudget), parsed._responseModelId ?? parsed.modelId, toolNsMap, freeform, toolSearch, () => {
998
1046
  const elapsed = Date.now() - loopT0;
999
1047
  if (executedSearchCount > 0 || searchesExecuted > 0) {
1000
1048
  console.warn(`[web-search-loop] cancelled — ${executedSearchCount} real searches, ${searchesExecuted - executedSearchCount} placeholders, ${elapsed}ms`);
@@ -1004,12 +1052,14 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
1004
1052
  {
1005
1053
  translatorBudget,
1006
1054
  replayCacheScope: parsed._reasoningReplayScope,
1055
+ ...(deps.clientToolCatalogRestriction ? { ...deps.clientToolCatalogRestriction, enforceDeclaredToolNames: false } : {}),
1007
1056
  ...(deps.forceEmptyResponseId ? { responseId: "" } : {}),
1057
+ ...(deps.terminalRefusalAsFailed ? { terminalRefusalAsFailed: true } : {}),
1008
1058
  hideThinkingSummary: parsed.options.hideThinkingSummary,
1009
1059
  hideRawReasoning: parsed.options.hideRawReasoning,
1010
1060
  ...(deps.stallTimeoutSec !== undefined ? { stallTimeoutSec: deps.stallTimeoutSec } : {}),
1011
1061
  ...(deps.onFirstOutput ? { onFirstOutput: deps.onFirstOutput } : {}),
1012
- ...(deps.onUsage ? { onUsage: deps.onUsage } : {}),
1062
+ ...(deps.onUsage ? { onUsage: (usage: OcxUsage | undefined) => deps.onUsage?.(mergeUsage(countersOnly(hiddenUsage), usage)) } : {}),
1013
1063
  ...(deps.onCompletedResponse ? { onCompletedResponse: deps.onCompletedResponse } : {}),
1014
1064
  },
1015
1065
  );
@@ -1,5 +1,5 @@
1
1
  import type { ProviderAdapter } from "../adapters/base";
2
- import type { AdapterEvent } from "../types";
2
+ import type { AdapterEvent, OcxUsage } from "../types";
3
3
  import {
4
4
  isTranslatorBudgetExceededError,
5
5
  TRANSLATOR_MAX_TURN_BYTES,
@@ -32,6 +32,8 @@ export interface ParseStreamWithProgressOptions {
32
32
  signal?: AbortSignal;
33
33
  /** Kept configurable for focused tests; production callers should use the 5 second default. */
34
34
  postTerminalDrainTimeoutMs?: number;
35
+ /** Receives the usage an adapter `error` event carries before the stream fails on it. */
36
+ onErrorUsage?: (usage: OcxUsage) => void;
35
37
  }
36
38
 
37
39
  type ParseStream = ProviderAdapter["parseStream"];
@@ -296,6 +298,7 @@ export async function* parseStreamWithProgress(
296
298
  );
297
299
  }
298
300
  if (event.type === "error") {
301
+ if (event.usage) options.onErrorUsage?.(event.usage);
299
302
  fail(event.code === "translation_buffer_limit"
300
303
  ? new TranslatorBudgetExceededError("live_transient", TRANSLATOR_MAX_TURN_BYTES)
301
304
  : new Error(event.message));
@@ -124,7 +124,12 @@ function extractIterationContent(events: AdapterEvent[]): OcxAssistantContentPar
124
124
  rawReasoning = "";
125
125
  };
126
126
  for (const e of events) {
127
- if (e.type === "thinking_delta") {
127
+ if (e.type === "text_delta" && e.anthropicThinkingSignature) {
128
+ flushText();
129
+ flushVisible();
130
+ flushRaw();
131
+ parts.push({ type: "thinking", thinking: e.text, signature: e.anthropicThinkingSignature });
132
+ } else if (e.type === "thinking_delta") {
128
133
  flushText();
129
134
  flushRaw();
130
135
  thinking += e.thinking;
@@ -533,6 +538,9 @@ export async function* runTurnWebSearchLoop(
533
538
  return;
534
539
  }
535
540
 
541
+ for (const event of replayEvents) {
542
+ if (event.type === "text_delta" && event.anthropicThinkingSignature) yield event;
543
+ }
536
544
  const iterationContent = extractIterationContent(split.passthrough);
537
545
  for (const [callIndex, call] of split.calls.entries()) {
538
546
  if (abortSignal?.aborted) { yield abortEvent(); return; }