@bitkyc08/opencodex 2.86.0 → 2.88.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 (400) hide show
  1. package/gui/dist/assets/App-CHC89EXQ.js +58 -0
  2. package/gui/dist/assets/App-wT3mXqFz.css +1 -0
  3. package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-B5bC7AWL.js} +1 -1
  4. package/gui/dist/assets/index-B5bsvxor.js +94 -0
  5. package/gui/dist/assets/index-DPeB9HpV.css +1 -0
  6. package/gui/dist/assets/quota-summary-D8y2wTsB.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-rejection-memo.ts +71 -0
  14. package/src/adapters/anthropic-replay.ts +135 -0
  15. package/src/adapters/anthropic-tool-catalog.ts +11 -0
  16. package/src/adapters/anthropic.ts +151 -33
  17. package/src/adapters/base.ts +2 -0
  18. package/src/adapters/claude-cli/adapter.ts +111 -23
  19. package/src/adapters/claude-cli/profiles.ts +2 -1
  20. package/src/adapters/claude-cli/stable-replay.ts +108 -0
  21. package/src/adapters/claude-cli/usage-admission.ts +230 -0
  22. package/src/adapters/coding-agent/protocol.ts +1 -1
  23. package/src/adapters/coding-agent/turn.ts +18 -2
  24. package/src/adapters/dsh-account.ts +97 -0
  25. package/src/adapters/google-progress-history.ts +53 -0
  26. package/src/adapters/google.ts +52 -9
  27. package/src/adapters/openai-chat/messages.ts +4 -3
  28. package/src/adapters/openai-chat/passthrough.ts +8 -2
  29. package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
  30. package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
  31. package/src/adapters/openai-responses/passthrough.ts +57 -125
  32. package/src/adapters/openai-responses/request-strips.ts +25 -3
  33. package/src/adapters/openai-responses/transport.ts +81 -0
  34. package/src/adapters/qoder/adapter.ts +4 -2
  35. package/src/adapters/registry.ts +5 -0
  36. package/src/adapters/zed.ts +26 -1
  37. package/src/advisor/activity.ts +55 -0
  38. package/src/advisor/budget.ts +62 -0
  39. package/src/advisor/consult.ts +4 -1
  40. package/src/advisor/context.ts +16 -4
  41. package/src/advisor/evidence-tool.ts +21 -0
  42. package/src/advisor/evidence.ts +117 -0
  43. package/src/advisor/runtime.ts +79 -31
  44. package/src/advisor/settings.ts +11 -2
  45. package/src/bridge/errors.ts +19 -1
  46. package/src/bridge/internal.ts +7 -1
  47. package/src/bridge/response-json.ts +18 -3
  48. package/src/bridge/sse.ts +59 -8
  49. package/src/bridge/terminal-refusal.ts +59 -0
  50. package/src/claude/desktop-3p-library.ts +32 -7
  51. package/src/claude/desktop-3p.ts +55 -13
  52. package/src/claude/desktop-first-party.ts +1 -1
  53. package/src/claude/desktop-picker-platform.ts +4 -0
  54. package/src/claude/desktop-picker.ts +15 -8
  55. package/src/claude/desktop-system-proxy.ts +50 -4
  56. package/src/claude/inbound-content-options.ts +77 -0
  57. package/src/claude/inbound.ts +33 -0
  58. package/src/claude/intercept/picker-ca-startup.ts +2 -1
  59. package/src/claude/intercept/picker-runtime.ts +6 -5
  60. package/src/claude/intercept/picker-trust-windows.ts +338 -0
  61. package/src/claude/intercept/picker-trust.ts +19 -0
  62. package/src/claude/intercept/port-policy.ts +30 -0
  63. package/src/claude/intercept/runtime.ts +97 -94
  64. package/src/claude/outbound.ts +46 -13
  65. package/src/cli/account-api.ts +1 -0
  66. package/src/cli/account-policy-dto.ts +6 -0
  67. package/src/cli/account-policy.ts +34 -5
  68. package/src/cli/account-preservation.ts +44 -0
  69. package/src/cli/account.ts +8 -2
  70. package/src/cli/advisor.ts +17 -3
  71. package/src/cli/capabilities-accounts.ts +20 -1
  72. package/src/cli/capabilities-agents-routing.ts +5 -2
  73. package/src/cli/capabilities-base.ts +38 -10
  74. package/src/cli/capabilities-observe-system.ts +63 -3
  75. package/src/cli/capabilities-provider-models.ts +11 -0
  76. package/src/cli/codex-cli-update.ts +151 -41
  77. package/src/cli/codex-provider.ts +44 -0
  78. package/src/cli/combo-input.ts +10 -1
  79. package/src/cli/combo-result.ts +1 -1
  80. package/src/cli/combo.ts +2 -1
  81. package/src/cli/cross-home-owner.ts +33 -16
  82. package/src/cli/dispatch.ts +25 -4
  83. package/src/cli/doctor.ts +6 -1
  84. package/src/cli/ensure-desktop-cold-start.ts +60 -7
  85. package/src/cli/help.ts +3 -2
  86. package/src/cli/index.ts +1 -1
  87. package/src/cli/integration-plan-dto.ts +1 -0
  88. package/src/cli/log-history.ts +172 -0
  89. package/src/cli/loopback-family.ts +31 -0
  90. package/src/cli/models-runtime-subcommands.ts +1 -0
  91. package/src/cli/models-runtime.ts +72 -8
  92. package/src/cli/observe-stream.ts +2 -0
  93. package/src/cli/observe.ts +16 -4
  94. package/src/cli/provider.ts +27 -5
  95. package/src/cli/registry.ts +28 -8
  96. package/src/cli/runtime-api.ts +5 -3
  97. package/src/cli/system-command.ts +40 -8
  98. package/src/cli/uninstall-client-state.ts +32 -0
  99. package/src/cli/usage-compare.ts +309 -0
  100. package/src/cli/usage-model-search.ts +4 -4
  101. package/src/cli/usage-report.ts +2 -2
  102. package/src/cli/v2.ts +1 -1
  103. package/src/client/machine-listener.ts +1 -3
  104. package/src/client/runtime.ts +1 -0
  105. package/src/clients/client-path.ts +51 -0
  106. package/src/clients/config-export/contracts.ts +2 -1
  107. package/src/clients/config-export.ts +18 -44
  108. package/src/clients/gentle-shell-path.ts +87 -0
  109. package/src/codex/account-auto-switch.ts +2 -0
  110. package/src/codex/account-identity.ts +29 -0
  111. package/src/codex/account-label.ts +1 -1
  112. package/src/codex/account-lifecycle.ts +21 -5
  113. package/src/codex/account-preservation.ts +28 -0
  114. package/src/codex/account-use-remaining.ts +23 -0
  115. package/src/codex/app-server-processes.ts +97 -7
  116. package/src/codex/app-server-restart-service.ts +55 -4
  117. package/src/codex/auth-api/account-list.ts +4 -0
  118. package/src/codex/auth-api/account-pause-group.ts +6 -50
  119. package/src/codex/auth-api/account-preservation.ts +57 -0
  120. package/src/codex/auth-api/routes.ts +8 -0
  121. package/src/codex/auth-api/use-remaining-route.ts +36 -0
  122. package/src/codex/auth-api.ts +0 -1
  123. package/src/codex/auth-context.ts +2 -2
  124. package/src/codex/catalog/auto-review.ts +20 -3
  125. package/src/codex/catalog/build-entries.ts +9 -1
  126. package/src/codex/catalog/combo-member.ts +14 -5
  127. package/src/codex/catalog/configured-tiers.ts +1 -1
  128. package/src/codex/catalog/gather-capture.ts +2 -0
  129. package/src/codex/catalog/metadata.ts +11 -9
  130. package/src/codex/catalog/model-hints.ts +21 -16
  131. package/src/codex/catalog/model-visibility.ts +3 -1
  132. package/src/codex/catalog/parsing.ts +30 -86
  133. package/src/codex/catalog/provider-models.ts +36 -34
  134. package/src/codex/catalog/routed-gather.ts +14 -13
  135. package/src/codex/cli-install-provenance.ts +105 -17
  136. package/src/codex/cli-update-plan.ts +545 -0
  137. package/src/codex/config-write-lock.ts +23 -2
  138. package/src/codex/desktop-app/picker-freshness.ts +96 -0
  139. package/src/codex/desktop-app-restart.ts +21 -0
  140. package/src/codex/desktop-compatibility/installed-build.ts +24 -6
  141. package/src/codex/desktop-compatibility/routing-preflight.ts +1 -1
  142. package/src/codex/desktop-compatibility/runtime.ts +12 -3
  143. package/src/codex/desktop-compatibility/windows-key-protection.ts +115 -9
  144. package/src/codex/features.ts +85 -45
  145. package/src/codex/inject/history-failure.ts +27 -0
  146. package/src/codex/inject/restore.ts +24 -2
  147. package/src/codex/inject/routing-classify.ts +4 -3
  148. package/src/codex/inject.ts +18 -27
  149. package/src/codex/main-account-hard-lock.ts +30 -1
  150. package/src/codex/native-profile-startup.ts +1 -1
  151. package/src/codex/ocx-compaction-history.ts +12 -10
  152. package/src/codex/paths.ts +86 -1
  153. package/src/codex/provider-routing.ts +174 -0
  154. package/src/codex/refresh.ts +2 -2
  155. package/src/codex/routing/health-store.ts +1 -1
  156. package/src/codex/routing/selection.ts +4 -3
  157. package/src/codex/routing/thread-affinity.ts +1 -1
  158. package/src/codex/signed-in-identity.ts +43 -0
  159. package/src/codex/subagent-model-fallback.ts +1 -1
  160. package/src/codex/sync.ts +6 -1
  161. package/src/codex/user-identity.ts +16 -5
  162. package/src/codex/websocket-activity.ts +161 -0
  163. package/src/codex/websocket-registry.ts +25 -1
  164. package/src/codex/windows-local-appdata.ts +84 -0
  165. package/src/combos/failover.ts +6 -1
  166. package/src/combos/index.ts +1 -0
  167. package/src/combos/resolve.ts +46 -10
  168. package/src/combos/types.ts +15 -0
  169. package/src/config/diagnostics.ts +33 -6
  170. package/src/config/live-reconcile.ts +38 -2
  171. package/src/config/load-degrade.ts +17 -0
  172. package/src/config/opencode-wire-upgrade.ts +54 -0
  173. package/src/config/opencode-zen-upgrade.ts +53 -0
  174. package/src/config/owner-registry.ts +22 -17
  175. package/src/config/provider-validation.ts +45 -2
  176. package/src/config/schema/config-schema.ts +22 -0
  177. package/src/config/schema/leaf-validators.ts +38 -9
  178. package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
  179. package/src/config/schema/native-reasoning-retention.ts +26 -0
  180. package/src/config.ts +2 -0
  181. package/src/generated/compatibility-version.json +657 -297
  182. package/src/grok/sync.ts +1 -1
  183. package/src/images/loop.ts +116 -27
  184. package/src/integrations/catalog-refresh.ts +1 -1
  185. package/src/integrations/mutation-plan.ts +1 -0
  186. package/src/integrations/registry.ts +8 -0
  187. package/src/lib/codex-restart-contract.ts +15 -0
  188. package/src/lib/desktop-upstream-tunnel.ts +18 -53
  189. package/src/lib/empty-tool-catalog.ts +62 -0
  190. package/src/lib/errors.ts +19 -3
  191. package/src/lib/gcp-adc.ts +96 -9
  192. package/src/lib/gcp-credential-input.ts +12 -0
  193. package/src/lib/lab-live-route-production.ts +5 -0
  194. package/src/lib/opaque-proxy-tunnel.ts +217 -0
  195. package/src/lib/redact.ts +2 -2
  196. package/src/lib/request-failure-model.ts +2 -0
  197. package/src/oauth/account-quota-rank.ts +69 -2
  198. package/src/oauth/anthropic-account-refusal.ts +20 -2
  199. package/src/oauth/anthropic-oauth-definitions.ts +3 -1
  200. package/src/oauth/anthropic-routing.ts +1 -21
  201. package/src/oauth/anthropic.ts +38 -7
  202. package/src/oauth/callback-server.ts +27 -1
  203. package/src/oauth/chatgpt-device.ts +1 -1
  204. package/src/oauth/chatgpt-shared.ts +83 -0
  205. package/src/oauth/chatgpt.ts +8 -72
  206. package/src/oauth/dsh.ts +203 -0
  207. package/src/oauth/generic-account-failover.ts +40 -11
  208. package/src/oauth/generic-session-affinity.ts +125 -0
  209. package/src/oauth/index.ts +184 -109
  210. package/src/oauth/local-token-detect.ts +21 -1
  211. package/src/oauth/login-cli-errors.ts +44 -0
  212. package/src/oauth/login-cli.ts +69 -7
  213. package/src/oauth/login-flow-state.ts +371 -23
  214. package/src/oauth/meta-muse-errors.ts +41 -0
  215. package/src/oauth/meta-muse-keychain.ts +52 -0
  216. package/src/oauth/meta-muse.ts +51 -84
  217. package/src/oauth/model-discovery-auth.ts +3 -0
  218. package/src/oauth/pool-settings-capability.ts +4 -2
  219. package/src/oauth/refresh-flight-retirement.ts +48 -0
  220. package/src/oauth/session-affinity-key.ts +23 -0
  221. package/src/oauth/store-anthropic-instance.ts +9 -2
  222. package/src/oauth/store.ts +12 -5
  223. package/src/protocols/encoders/adapter-events.ts +6 -5
  224. package/src/protocols/encoders/messages.ts +9 -6
  225. package/src/protocols/provider-summary.ts +2 -1
  226. package/src/providers/api-key-cooldowns.ts +49 -0
  227. package/src/providers/api-key-resolve.ts +11 -6
  228. package/src/providers/api-key-response-ownership.ts +57 -0
  229. package/src/providers/api-key-selection.ts +31 -6
  230. package/src/providers/api-keys.ts +214 -3
  231. package/src/providers/claude-cli-usage.ts +96 -0
  232. package/src/providers/dsh-account-destination.ts +31 -0
  233. package/src/providers/dsh-account-models.ts +41 -0
  234. package/src/providers/gcp-credential-cleanup.ts +59 -0
  235. package/src/providers/key-failover.ts +54 -50
  236. package/src/providers/key-store.ts +7 -2
  237. package/src/providers/model-rename-fields.ts +3 -0
  238. package/src/providers/model-rename-migration.ts +2 -2
  239. package/src/providers/openai-tiers-destination.ts +11 -2
  240. package/src/providers/openai-tiers.ts +5 -12
  241. package/src/providers/opencode-go-transport.ts +3 -1
  242. package/src/providers/opencode-zen-rate-limit.ts +7 -2
  243. package/src/providers/quota/account-cache.ts +6 -5
  244. package/src/providers/quota/account-row.ts +34 -0
  245. package/src/providers/quota/antigravity.ts +28 -7
  246. package/src/providers/quota/dsh-account.ts +70 -0
  247. package/src/providers/quota/vendor-probes-oauth.ts +1 -93
  248. package/src/providers/quota-types.ts +2 -0
  249. package/src/providers/quota.ts +14 -20
  250. package/src/providers/registry/entries-core.ts +37 -8
  251. package/src/providers/registry/types.ts +1 -1
  252. package/src/providers/request-pacing.ts +77 -19
  253. package/src/providers/routed-config.ts +339 -0
  254. package/src/providers/service-tier.ts +2 -1
  255. package/src/quota/window-mapping.ts +5 -2
  256. package/src/responses/anthropic-progress.ts +34 -0
  257. package/src/responses/assistant-replay.ts +35 -0
  258. package/src/responses/compaction-history-framing.ts +65 -0
  259. package/src/responses/compaction.ts +6 -1
  260. package/src/responses/incomplete-reason.ts +31 -0
  261. package/src/responses/parser.ts +50 -5
  262. package/src/responses/reasoning-envelope.ts +4 -0
  263. package/src/responses/reasoning-replay-cache.ts +74 -6
  264. package/src/responses/reasoning-retention.ts +173 -0
  265. package/src/responses/retained-compaction.ts +46 -0
  266. package/src/responses/schema.ts +23 -0
  267. package/src/router.ts +8 -321
  268. package/src/routing/analytics.ts +7 -15
  269. package/src/routing/health.ts +37 -22
  270. package/src/routing/history/indexer.ts +6 -5
  271. package/src/routing/history/ingest.ts +6 -2
  272. package/src/routing/profile.ts +1 -1
  273. package/src/server/admission-model-scope.ts +17 -0
  274. package/src/server/auth-cors.ts +11 -0
  275. package/src/server/chat-completions.ts +7 -1
  276. package/src/server/chat-native-reasoning.ts +123 -0
  277. package/src/server/chat-native-sse.ts +110 -7
  278. package/src/server/chat-native.ts +76 -17
  279. package/src/server/claude-messages.ts +56 -414
  280. package/src/server/effort-policy.ts +1 -1
  281. package/src/server/images.ts +15 -2
  282. package/src/server/index/live-sideband.ts +3 -2
  283. package/src/server/index/model-alias-id.ts +36 -0
  284. package/src/server/index/optional-listeners.ts +2 -0
  285. package/src/server/index/serve-options.ts +20 -6
  286. package/src/server/index/startup-warnings.ts +2 -0
  287. package/src/server/index/websocket-handler.ts +18 -4
  288. package/src/server/index.ts +8 -8
  289. package/src/server/inference/advisor-diagnostics.ts +31 -0
  290. package/src/server/lifecycle.ts +13 -1
  291. package/src/server/management/advisor-routes.ts +15 -4
  292. package/src/server/management/claude-desktop-picker-routes.ts +1 -1
  293. package/src/server/management/combo-routes.ts +176 -50
  294. package/src/server/management/companion-routes.ts +1 -1
  295. package/src/server/management/config-routes.ts +75 -4
  296. package/src/server/management/cursor-integration-routes.ts +1 -1
  297. package/src/server/management/model-routes.ts +11 -6
  298. package/src/server/management/model-rows.ts +14 -0
  299. package/src/server/management/native-integration-routes.ts +2 -7
  300. package/src/server/management/native-reasoning-retention-routes.ts +41 -0
  301. package/src/server/management/oauth-account-routes.ts +87 -14
  302. package/src/server/management/provider-gcp-credential-routes.ts +29 -0
  303. package/src/server/management/provider-key-pacing-routes.ts +31 -0
  304. package/src/server/management/provider-overwrite-carry.ts +7 -0
  305. package/src/server/management/provider-routes.ts +25 -30
  306. package/src/server/management/route-registry.ts +10 -1
  307. package/src/server/management-api.ts +3 -1
  308. package/src/server/messages-native-tool-guard.ts +185 -0
  309. package/src/server/messages-native.ts +117 -24
  310. package/src/server/messages-shared.ts +431 -0
  311. package/src/server/relay.ts +1 -1
  312. package/src/server/request-log-terminal-status.ts +1 -1
  313. package/src/server/request-log.ts +6 -1
  314. package/src/server/responses/account-change-state.ts +8 -2
  315. package/src/server/responses/adapter-continuation.ts +47 -19
  316. package/src/server/responses/adapter-delivery.ts +72 -12
  317. package/src/server/responses/adapter-dispatch.ts +46 -3
  318. package/src/server/responses/advisor-retention.ts +117 -0
  319. package/src/server/responses/advisor-slot.ts +73 -41
  320. package/src/server/responses/anthropic-thinking-recovery.ts +153 -0
  321. package/src/server/responses/codex-ws-wire.ts +15 -8
  322. package/src/server/responses/combo-stream-preflight.ts +3 -2
  323. package/src/server/responses/compact.ts +303 -125
  324. package/src/server/responses/compaction-recovery.ts +20 -10
  325. package/src/server/responses/compaction-retention.ts +61 -0
  326. package/src/server/responses/context-overflow.ts +11 -6
  327. package/src/server/responses/core-auth.ts +6 -0
  328. package/src/server/responses/core-codex-account.ts +11 -1
  329. package/src/server/responses/core-combo-failure.ts +2 -0
  330. package/src/server/responses/core-combo.ts +121 -36
  331. package/src/server/responses/core-errors.ts +3 -3
  332. package/src/server/responses/core-lifetime.ts +8 -4
  333. package/src/server/responses/core-opaque-recovery.ts +35 -0
  334. package/src/server/responses/core-options.ts +7 -0
  335. package/src/server/responses/core-pipeline.ts +141 -0
  336. package/src/server/responses/core-replay.ts +23 -3
  337. package/src/server/responses/core.ts +2 -115
  338. package/src/server/responses/fetch-helpers.ts +73 -19
  339. package/src/server/responses/main-hard-lock-routing.ts +109 -0
  340. package/src/server/responses/native-response-control.ts +4 -2
  341. package/src/server/responses/passthrough-delivery.ts +3 -1
  342. package/src/server/responses/passthrough-dispatch.ts +15 -13
  343. package/src/server/responses/passthrough-execution.ts +3 -2
  344. package/src/server/responses/request-prepare.ts +77 -12
  345. package/src/server/responses/request-sidecar-auth.ts +2 -2
  346. package/src/server/responses/request-transport.ts +104 -27
  347. package/src/server/responses/response-effects.ts +2 -0
  348. package/src/server/responses/response-tool-ownership.ts +21 -0
  349. package/src/server/responses/run-turn-execution.ts +96 -30
  350. package/src/server/responses/sidecar-execution.ts +63 -9
  351. package/src/server/responses/sidecar-send-budget.ts +2 -2
  352. package/src/server/responses/terminal-guard.ts +2 -16
  353. package/src/server/responses-undeclared-tool-guard.ts +1 -1
  354. package/src/server/ws-bridge.ts +2 -0
  355. package/src/service/auth-preflight.ts +71 -0
  356. package/src/service/cli.ts +4 -2
  357. package/src/service/definition.ts +97 -0
  358. package/src/service/guards.ts +1 -61
  359. package/src/service/health-sentinel-linux.ts +112 -0
  360. package/src/service/health-sentinel-main.ts +76 -57
  361. package/src/service/health.ts +5 -89
  362. package/src/service/launchd.ts +27 -7
  363. package/src/service/logging.ts +72 -9
  364. package/src/service/repair.ts +2 -1
  365. package/src/service/state.ts +7 -8
  366. package/src/service/systemd.ts +1 -1
  367. package/src/service/windows-guarded-manager.ts +2 -1
  368. package/src/service/windows-ops.ts +1 -1
  369. package/src/service/windows-scheduler.ts +2 -7
  370. package/src/service/windows-taskxml.ts +2 -2
  371. package/src/service/windows-wscript.ts +14 -0
  372. package/src/service.ts +4 -2
  373. package/src/sidecar/auth.ts +2 -0
  374. package/src/types/config.ts +64 -3
  375. package/src/types/opencode-go.ts +61 -0
  376. package/src/types/opencode-zen.ts +35 -0
  377. package/src/types/provider.ts +32 -6
  378. package/src/types/request.ts +31 -2
  379. package/src/types/wire.ts +31 -37
  380. package/src/types.ts +2 -0
  381. package/src/update/npm-invocation.mjs +12 -2
  382. package/src/usage/cost.ts +48 -12
  383. package/src/usage/expected-prices.ts +3 -0
  384. package/src/usage/ledger-retention.ts +2 -1
  385. package/src/usage/ledger-scanner.ts +6 -5
  386. package/src/usage/log.ts +17 -8
  387. package/src/usage/summary.ts +30 -11
  388. package/src/usage/telemetry-contract.ts +1 -0
  389. package/src/usage/user-cost-overlays.ts +14 -19
  390. package/src/usage/user-cost-validation.ts +38 -0
  391. package/src/vision/eligibility.ts +13 -0
  392. package/src/web-search/anthropic-history.ts +59 -0
  393. package/src/web-search/loop.ts +236 -73
  394. package/src/web-search/progress-stream.ts +15 -2
  395. package/src/web-search/run-turn-loop.ts +9 -1
  396. package/gui/dist/assets/App-BjlttVLy.js +0 -58
  397. package/gui/dist/assets/App-CSJvmjNs.css +0 -1
  398. package/gui/dist/assets/index-BB6uK6tU.css +0 -1
  399. package/gui/dist/assets/index-BMssP4k3.js +0 -94
  400. package/gui/dist/assets/quota-summary-D8M1bc_R.js +0 -1
@@ -1,3 +1,7 @@
1
+ import { captureAnthropicRejectionPayloads, keepAnthropicReplayBlock } from "./anthropic-rejection-memo";
2
+ import { anthropicDeclaredTools } from "./anthropic-tool-catalog";
3
+ import { anthropicReplayContent, createAnthropicReplayCapture } from "./anthropic-replay";
4
+ import { withAnthropicProgressUpdates } from "./anthropic-progress";
1
5
  import type { IncomingMeta, ProviderAdapter } from "./base";
2
6
  import { createAdapterTierMetadata, type AdapterTierMetadata } from "../providers/fastwire";
3
7
  import { ANTHROPIC_FAST_MODE_BETA, mergeAnthropicBetaHeader } from "../providers/anthropic-fast";
@@ -16,7 +20,7 @@ import type {
16
20
  OcxToolResultMessage,
17
21
  OcxUsage,
18
22
  } from "../types";
19
- import { isAllowedToolChoice, namespacedToolName, resolveToolChoiceWireName, toolChoiceToolPredicate } from "../types";
23
+ import { isAllowedToolChoice, namespacedToolName, resolveToolChoiceWireName } from "../types";
20
24
  import { ANTHROPIC_OAUTH_BETA, CLAUDE_CODE_SYSTEM_INSTRUCTION, applyClaudeToolPrefix, stripClaudeToolPrefix } from "../oauth/anthropic";
21
25
  import { parseDataUrl } from "./image";
22
26
  import { enforceAnthropicImageLimits } from "./anthropic-image-guard";
@@ -31,7 +35,7 @@ import { decodeServerSentEvents } from "../lib/sse-decoder";
31
35
  import { isTranslatorBudgetExceededError, retainTranslatedEventBatch, type TranslatorBudget } from "../lib/translator-budget";
32
36
  import { isReasoningEffortOmitted, modelRecordValue } from "../reasoning-effort";
33
37
  import { applyAgentRouterLanguageFraming, isAgentRouterEndpoint } from "./agentrouter";
34
- import { rejectsCombinedSampling, rejectsForcedToolChoice, rejectsSamplingParameters, supportsExplicitThinkingDisable, usesAdaptiveThinking, usesBetweenToolsFloor } from "./anthropic-model-contract";
38
+ import { claudeFamilyVersion, rejectsCombinedSampling, rejectsForcedToolChoice, rejectsSamplingParameters, supportsExplicitThinkingDisable, usesAdaptiveThinking, usesBetweenToolsFloor } from "./anthropic-model-contract";
35
39
 
36
40
  /** Map a user content part to an Anthropic content block (text or image source). */
37
41
  function toAnthropicContentPart(p: OcxContentPart): unknown {
@@ -282,7 +286,7 @@ function normalizeTtlOrdering(body: Record<string, unknown>): void {
282
286
  }
283
287
  }
284
288
 
285
- function isLikelyRealAnthropicThinkingSignature(signature: string | undefined): signature is string {
289
+ export function isLikelyRealAnthropicThinkingSignature(signature: string | undefined): signature is string {
286
290
  if (typeof signature !== "string" || signature.length < 16) return false;
287
291
  if (/^(fc|call|msg|rs|resp|reasoning|item|ws|tool|func|function)[-_]/i.test(signature)) return false;
288
292
  return /^[A-Za-z0-9+/_=-]+$/.test(signature);
@@ -312,11 +316,12 @@ function isAnthropicContentFilterStopReason(
312
316
  }
313
317
 
314
318
  /**
315
- * Anthropic `refusal` / `content_filter` is a permanent sampling decision, not a disconnect.
316
- * Emitting `done` with that stopReason used to surface as `response.incomplete` without
317
- * `retryable`, which Codex treats as a dropped stream and retries five times (#4312).
318
- * The explicit incomplete event is what the bridge already forwards into
319
- * `incomplete_details.retryable`. Usage is preserved: a filtered turn still consumed tokens.
319
+ * Anthropic `refusal` / `content_filter` is a permanent sampling decision, not a disconnect
320
+ * (#4312). Codex does not read `incomplete_details.retryable`: it retries every
321
+ * `response.incomplete`. The explicit `retryable: false` verdict here is the marker the bridge
322
+ * uses to deliver the turn to a Responses client as a terminal `response.failed` instead
323
+ * (`src/bridge/terminal-refusal.ts`); a `done` with that stopReason carries no verdict and
324
+ * stays a plain incomplete. Usage is preserved: a filtered turn still consumed tokens.
320
325
  * `max_tokens` stays a `done` so the client can continue from a legitimate truncation.
321
326
  */
322
327
  function anthropicContentFilterIncomplete(
@@ -520,6 +525,16 @@ function streamedToolArgumentsParse(assembled: string): boolean {
520
525
  }
521
526
  }
522
527
 
528
+ /** A server tool's complete streamed input; undefined when it cannot be recovered. */
529
+ function serverToolInput(json: string): Record<string, unknown> | undefined {
530
+ try {
531
+ const input = JSON.parse(json) as unknown;
532
+ return isAnthropicRecord(input) ? input : undefined;
533
+ } catch {
534
+ return undefined;
535
+ }
536
+ }
537
+
523
538
  function anthropicKeyUsesBearer(provider: OcxProviderConfig): boolean {
524
539
  return provider.apiKeyTransport === "bearer";
525
540
  }
@@ -723,7 +738,16 @@ function orphanToolResultContent(msg: OcxToolResultMessage): string | unknown[]
723
738
  function messagesToAnthropicFormat(
724
739
  parsed: OcxParsedRequest,
725
740
  toolNames: { toWire: (name: string) => string },
741
+ serverReplay?: ReadonlyMap<string, readonly Record<string, unknown>[]>,
726
742
  ): { system: string | undefined; messages: unknown[] } {
743
+ // The client's server-tool exchanges go back ahead of the block they preceded. Their anchors are
744
+ // unique in the caller's history (replayedAnthropicServerBlocks drops repeated ones); each is used once.
745
+ const pendingReplay = new Map(serverReplay);
746
+ const replayed = (anchor: string) => {
747
+ const blocks = pendingReplay.get(anchor) ?? [];
748
+ pendingReplay.delete(anchor);
749
+ return blocks;
750
+ };
727
751
  // One allocator for the whole request: a tool_result must resolve to the SAME wire id its
728
752
  // call got, and two distinct raw ids must never collapse into one. Conforming ids are claimed
729
753
  // first so a rewritten id can never squat on an id another call legitimately owns.
@@ -738,8 +762,8 @@ function messagesToAnthropicFormat(
738
762
  }
739
763
  }
740
764
  const toolCatalogNudge = buildNonOpenAIToolCatalogNudgeForTools(
741
- parsed.context.tools,
742
- parsed.options.toolChoice,
765
+ anthropicDeclaredTools(parsed),
766
+ undefined,
743
767
  tool => toolNames.toWire(namespacedToolName(tool.namespace, tool.name)),
744
768
  );
745
769
  const systemParts = [...(parsed.context.systemPrompt ?? []), ...(toolCatalogNudge ? [toolCatalogNudge] : [])];
@@ -769,27 +793,37 @@ function messagesToAnthropicFormat(
769
793
  case "assistant": {
770
794
  const aMsg = msg as OcxAssistantMessage;
771
795
  const preface: unknown[] = [];
796
+
772
797
  const toolUses: unknown[] = [];
798
+ const originalOrder: unknown[] = [];
799
+ const appendPreface = (block: unknown) => { preface.push(block); originalOrder.push(block); };
800
+ let signed = false;
773
801
  const toolUseIds: string[] = [];
774
802
  for (const part of aMsg.content) {
775
803
  if (part.type === "text") {
776
804
  const text = (part as OcxTextContent).text;
777
- if (text) preface.push({ type: "text", text });
805
+ for (const b of replayed(`text:${text}`)) appendPreface(b);
806
+ if (text) appendPreface({ type: "text", text });
778
807
  } else if (part.type === "thinking") {
779
808
  const t = part as OcxThinkingContent;
809
+ // Server blocks that preceded this reasoning keep their place, even when it is dropped below.
810
+ for (const data of t.redacted ?? []) for (const b of replayed(`redacted:${data}`)) appendPreface(b);
811
+ for (const b of replayed(`thinking:${t.signature}`)) appendPreface(b);
780
812
  // History minted under another serving identity (or already rejected as opaque) is not
781
813
  // this destination's to verify: drop its opaque blocks, as the Responses passthrough does.
782
814
  if (parsed._stripReasoningEncryptedContent === true) continue;
783
815
  // Redacted blocks replay verbatim FIRST (they preceded the visible thinking block
784
816
  // in the original stream order preserved by the bridge envelope).
785
817
  for (const data of t.redacted ?? []) {
786
- preface.push({ type: "redacted_thinking", data });
818
+ appendPreface({ type: "redacted_thinking", data });
787
819
  }
788
820
  if (isLikelyRealAnthropicThinkingSignature(t.signature)) {
789
- preface.push({ type: "thinking", thinking: t.thinking, signature: t.signature });
821
+ signed = true;
822
+ appendPreface({ type: "thinking", thinking: t.thinking, signature: t.signature });
790
823
  }
791
824
  } else if (part.type === "toolCall") {
792
825
  const tc = part as OcxToolCall;
826
+ for (const b of replayed(`call:${tc.id}`)) appendPreface(b);
793
827
  const flatName = namespacedToolName(tc.namespace, tc.name);
794
828
  // Normalized here, and identically for the matching tool_result above, so a history
795
829
  // replayed from another provider path keeps its call/result pairing (#1767).
@@ -797,16 +831,26 @@ function messagesToAnthropicFormat(
797
831
  // rejects. An unrepresentable call becomes text instead, and its result follows it there.
798
832
  const wireCallId = callIds.allocate(tc.id);
799
833
  if (wireCallId === undefined) {
800
- preface.push({ type: "text", text: unrepresentableToolCallText(tc, toolNames.toWire(flatName)) });
834
+ appendPreface({ type: "text", text: unrepresentableToolCallText(tc, toolNames.toWire(flatName)) });
801
835
  continue;
802
836
  }
803
837
  toolUseIds.push(wireCallId);
804
- toolUses.push({ type: "tool_use", id: wireCallId, name: toolNames.toWire(flatName), input: tc.arguments });
838
+ const block = { type: "tool_use", id: wireCallId, name: toolNames.toWire(flatName), input: tc.arguments };
839
+ toolUses.push(block);
840
+ originalOrder.push(block);
805
841
  }
806
842
  }
807
843
  // Anthropic treats text/thinking after tool_use as ending the tool turn, which makes
808
844
  // earlier tool_use ids look unpaired (#620 / common multi-step history shape).
809
- const content = [...preface, ...toolUses];
845
+ const captured = anthropicReplayContent(aMsg);
846
+ if (captured) {
847
+ const expected = toolUses as Array<{ id: string; name: string }>;
848
+ const actual = captured.filter(block => block.type === "tool_use");
849
+ if (actual.length !== expected.length || actual.some((block, index) => block.id !== expected[index]?.id || block.name !== expected[index]?.name)) {
850
+ throw new Error("Anthropic search replay tool identity changed");
851
+ }
852
+ }
853
+ const content = (captured ?? (signed ? originalOrder : [...preface, ...toolUses])).filter(block => keepAnthropicReplayBlock(parsed, block as Record<string, unknown>));
810
854
  if (content.length === 0) break;
811
855
  messages.push({ role: "assistant", content });
812
856
  if (toolUseIds.length > 0) {
@@ -869,9 +913,7 @@ function messagesToAnthropicFormat(
869
913
 
870
914
  function toolsToAnthropicFormat(parsed: OcxParsedRequest, toolNames: { toWire: (name: string) => string }): unknown[] | undefined {
871
915
  if (!parsed.context.tools || parsed.context.tools.length === 0) return undefined;
872
- const tools = isAllowedToolChoice(parsed.options.toolChoice)
873
- ? parsed.context.tools.filter(toolChoiceToolPredicate(parsed.options.toolChoice, parsed.context.tools))
874
- : parsed.context.tools;
916
+ const tools = anthropicDeclaredTools(parsed);
875
917
  if (tools.length === 0) return undefined;
876
918
  const converted = tools.map(t => ({
877
919
  name: toolNames.toWire(namespacedToolName(t.namespace, t.name)),
@@ -887,6 +929,18 @@ function toolsToAnthropicFormat(parsed: OcxParsedRequest, toolNames: { toWire: (
887
929
  return converted;
888
930
  }
889
931
 
932
+ /**
933
+ * Appends the Claude client's typed web tools (`parsed._anthropicServerTools`) so Anthropic runs
934
+ * them server-side. A same-named function tool wins: the web-search sidecar's `web_search` keeps
935
+ * priority, and Anthropic rejects duplicate tool names.
936
+ */
937
+ function withAnthropicServerTools(tools: unknown[] | undefined, parsed: OcxParsedRequest): unknown[] | undefined {
938
+ const serverTools = parsed._anthropicServerTools?.filter(t => !parsed.context.tools?.some(f => f.name === t.name));
939
+ if (!serverTools?.length) return tools;
940
+ // The caller's breakpoints are dropped on this lane; applyPromptCaching places its own.
941
+ return [...(tools ?? []), ...serverTools.map(t => ({ ...t, cache_control: undefined }))];
942
+ }
943
+
890
944
  function normalizeAnthropicInputSchema(schema: unknown): Record<string, unknown> {
891
945
  const stripped = stripResponsesOnlyEncryptedMarker(schema);
892
946
  const obj = stripped && typeof stripped === "object" && !Array.isArray(stripped)
@@ -952,7 +1006,8 @@ function normalizeAnthropicInputSchema(schema: unknown): Record<string, unknown>
952
1006
  export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetention?: "none" | "short" | "long"): ProviderAdapter {
953
1007
  const isOAuth = provider.authMode === "oauth";
954
1008
  const toolNames = buildToolNameTransforms(provider);
955
- return {
1009
+ let progressUpdates = false;
1010
+ return withAnthropicProgressUpdates({
956
1011
  name: "anthropic",
957
1012
 
958
1013
  formatErrorBody: formatAnthropicErrorBody,
@@ -965,7 +1020,9 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
965
1020
  throw new Error("anthropic provider requires a non-empty apiKey (authMode: key)");
966
1021
  }
967
1022
 
968
- const { system, messages } = messagesToAnthropicFormat(parsed, toolNames);
1023
+ // Only api.anthropic.com runs Anthropic's server tools, so only it gets their history back.
1024
+ const { system, messages } = messagesToAnthropicFormat(parsed, toolNames,
1025
+ usesNativeAnthropicEndpoint(provider) ? parsed._anthropicServerReplay : undefined);
969
1026
  // Before image normalization, so the framing block is present for every downstream pass.
970
1027
  if (isAgentRouterEndpoint(provider.baseUrl)) applyAgentRouterLanguageFraming(messages);
971
1028
  // Primary image layer: resize/re-encode to fit Anthropic limits without dropping
@@ -978,7 +1035,9 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
978
1035
  // Anthropic rejects many-image requests (>20 images) carrying any image over
979
1036
  // 2000px per side; see anthropic-image-guard.ts for the full limit policy.
980
1037
  enforceAnthropicImageLimits(messages);
981
- const tools = toolsToAnthropicFormat(parsed, toolNames);
1038
+ const converted = toolsToAnthropicFormat(parsed, toolNames);
1039
+ // A third-party Anthropic-compatible endpoint may not implement Anthropic's server tools.
1040
+ const tools = usesNativeAnthropicEndpoint(provider) ? withAnthropicServerTools(converted, parsed) : converted;
982
1041
 
983
1042
  // Codex never sends `max_output_tokens`, so the omitted-limit default decides how
984
1043
  // long a Claude answer may run. Honor the provider's configured output budget
@@ -1017,6 +1076,8 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1017
1076
  // anyway, and thinking shares the caller's `max_tokens` — which truncates a small-budget
1018
1077
  // request before it can emit its stop sequence (#545). Say "disabled" out loud where the
1019
1078
  // model both defaults to thinking and accepts being told not to.
1079
+ const family = claudeFamilyVersion(parsed.modelId);
1080
+ progressUpdates = usesNativeAnthropicEndpoint(provider) && family?.family === "opus" && family.major === 5 && family.minor === 5;
1020
1081
  const effectiveReasoning = parsed.options.reasoning ?? defaultReasoningEffort(provider, parsed.modelId);
1021
1082
  if (effectiveReasoning === "none" && supportsExplicitThinkingDisable(parsed.modelId)) {
1022
1083
  body.thinking = { type: "disabled" };
@@ -1034,7 +1095,8 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1034
1095
  // default 8192-token request can spend everything on thought and return empty text.
1035
1096
  // Opus 4.7+ defaults `display` to "omitted": the stream then carries signature-only
1036
1097
  // thinking blocks, so a Chat client sees minutes of heartbeats and no reasoning delta
1037
- // during a long think (#5824). Ask for summarized thinking unless the caller hides it.
1098
+ // during a long think (#5824). Opus 5.5 uses the documented updates discriminator;
1099
+ // other models request summarized thinking unless the caller hides it.
1038
1100
  body.thinking = parsed.options.hideThinkingSummary
1039
1101
  ? { type: "adaptive" }
1040
1102
  : { type: "adaptive", display: "summarized" };
@@ -1082,6 +1144,8 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1082
1144
  delete body.top_p;
1083
1145
  }
1084
1146
 
1147
+ if (progressUpdates) body.thinking = { type: "adaptive", display: "updates" };
1148
+
1085
1149
  if (rejectsSamplingParameters(parsed.modelId)) {
1086
1150
  // Opus 4.7+, Sonnet 5+, Haiku 5.5+ and Fable 400 on non-default sampling, with or without
1087
1151
  // thinking (anthropic-model-contract.ts).
@@ -1155,7 +1219,7 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1155
1219
  if (isOAuth) applyAnthropicOAuthAuth(headers, provider.apiKey);
1156
1220
  else applyAnthropicKeyAuth(headers, provider);
1157
1221
  if (provider.headers) Object.assign(headers, provider.headers);
1158
- mergeAnthropicBetaHeader(headers, fastSpeed?.betas ?? []);
1222
+ mergeAnthropicBetaHeader(headers, [...(fastSpeed?.betas ?? []), ...(progressUpdates ? ["thinking-display-updates-2026-08-18"] : [])]);
1159
1223
 
1160
1224
  // Prompt caching: native Anthropic supports top-level automatic caching, which
1161
1225
  // follows the moving final block across turns. Keep one breakpoint slot free for it.
@@ -1170,7 +1234,7 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1170
1234
  enforceCacheControlLimit(body, explicitLimit);
1171
1235
  normalizeTtlOrdering(body);
1172
1236
 
1173
- return {
1237
+ const request = {
1174
1238
  url,
1175
1239
  method: "POST",
1176
1240
  headers,
@@ -1184,6 +1248,8 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1184
1248
  fastSpeed?.value ?? null,
1185
1249
  ),
1186
1250
  };
1251
+ captureAnthropicRejectionPayloads(request, body, incoming?.translatorBudget);
1252
+ return request;
1187
1253
  },
1188
1254
 
1189
1255
  async *parseStream(
@@ -1196,6 +1262,7 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1196
1262
  return;
1197
1263
  }
1198
1264
 
1265
+ const replayCapture = createAnthropicReplayCapture(response, budget);
1199
1266
  const budgetEncoder = new TextEncoder();
1200
1267
  let currentBlockType = "";
1201
1268
  let currentToolCallId = "";
@@ -1205,6 +1272,8 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1205
1272
  let pendingStopReason: string | undefined;
1206
1273
  let emittedDone = false;
1207
1274
  let sawVisibleText = false;
1275
+ /** The open server-side web_search / web_fetch call, until its result block. */
1276
+ let serverSearch: { id: string; json: string; streaming: boolean } | undefined;
1208
1277
 
1209
1278
  const emitDone = function* (): Generator<AdapterEvent> {
1210
1279
  if (emittedDone) return;
@@ -1222,17 +1291,19 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1222
1291
  return;
1223
1292
  }
1224
1293
  // Refusal / content_filter must not look like a dropped stream. `done` with that
1225
- // stopReason becomes `response.incomplete` without `retryable`, and Codex retries
1226
- // the same refusal five times (#4312).
1294
+ // stopReason carries no verdict, so the bridge could not tell it from a retryable
1295
+ // incomplete and Codex would re-send the same refusal (#4312).
1227
1296
  if (isAnthropicContentFilterStopReason(pendingStopReason)) {
1228
1297
  yield anthropicContentFilterIncomplete(pendingStopReason, usageFromAnthropic(pendingUsage));
1229
1298
  return;
1230
1299
  }
1231
- yield {
1300
+ const done: AdapterEvent = {
1232
1301
  type: "done",
1233
1302
  usage: usageFromAnthropic(pendingUsage),
1234
1303
  ...(pendingStopReason ? { stopReason: pendingStopReason } : {}),
1235
1304
  };
1305
+ replayCapture?.finish(done);
1306
+ yield done;
1236
1307
  };
1237
1308
 
1238
1309
  try {
@@ -1252,6 +1323,7 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1252
1323
  try {
1253
1324
  parsed = JSON.parse(payload);
1254
1325
  } catch {
1326
+ replayCapture?.invalidate();
1255
1327
  debugDroppedFrame("anthropic", payload);
1256
1328
  continue;
1257
1329
  }
@@ -1259,10 +1331,12 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1259
1331
  // it and the `data.type` read below crashed the stream. Drop a non-record frame the same
1260
1332
  // way an unparseable one is dropped, so the message_stop check still governs the outcome.
1261
1333
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
1334
+ replayCapture?.invalidate();
1262
1335
  debugDroppedFrame("anthropic", payload);
1263
1336
  continue;
1264
1337
  }
1265
1338
  const data = parsed as Record<string, unknown>;
1339
+ replayCapture?.accept({ ...data, type: record.event || data.type });
1266
1340
 
1267
1341
  switch (record.event || data.type) {
1268
1342
  case "message_start": {
@@ -1273,8 +1347,11 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1273
1347
  break;
1274
1348
  }
1275
1349
  case "content_block_start": {
1276
- const block = data.content_block as { type: string; id?: string; name?: unknown; data?: string; thinking?: string } | undefined;
1350
+ const block = data.content_block as { type: string; id?: string; name?: unknown; data?: string; thinking?: string; tool_use_id?: unknown; content?: unknown } | undefined;
1277
1351
  if (!block) break;
1352
+ // Even an unmodeled server tool or empty block is semantic progress.
1353
+ // Fence replays before a later refusal; unknown blocks must not look like pings.
1354
+ yield { type: "heartbeat", replayUnsafe: true };
1278
1355
  currentBlockType = block.type;
1279
1356
  if (block.type === "thinking") {
1280
1357
  // Preserve even a display:omitted block boundary. The bridge can then
@@ -1288,6 +1365,36 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1288
1365
  budget.openCall(currentToolCallId);
1289
1366
  yield { type: "tool_call_start", id: currentToolCallId, name: currentToolCallName };
1290
1367
  }
1368
+ if (block.type === "server_tool_use" && (block.name === "web_search" || block.name === "web_fetch") && typeof block.id === "string") {
1369
+ // Only the input (query / url) is collected here (input_json_delta below). The cell
1370
+ // opens at its result: a call Anthropic defers beside a client tool_use has
1371
+ // no result in this response and must not surface as a completed search.
1372
+ serverSearch = { id: block.id, json: "", streaming: true };
1373
+ }
1374
+ if ((block.type === "web_search_tool_result" || block.type === "web_fetch_tool_result") && typeof block.tool_use_id === "string") {
1375
+ // The begin/end pair the sidecar emits, plus both blocks verbatim so the Claude client holds a
1376
+ // replayable copy. Without this response's matching call and complete input, drop the
1377
+ // exchange instead of inventing a call that could poison the client's next request.
1378
+ const fetch = block.type === "web_fetch_tool_result";
1379
+ const input = serverSearch?.id === block.tool_use_id ? serverToolInput(serverSearch.json) : undefined;
1380
+ if (!input) break;
1381
+ const hits = Array.isArray(block.content) ? block.content as Array<{ url?: unknown; title?: unknown }> : undefined;
1382
+ yield { type: "web_search_call_begin", id: block.tool_use_id };
1383
+ yield {
1384
+ type: "web_search_call_end",
1385
+ id: block.tool_use_id,
1386
+ queries: typeof input.query === "string" && input.query.length > 0 ? [input.query] : [],
1387
+ // A failed call carries one error object instead of the hit list / fetched page.
1388
+ status: (fetch ? (block.content as { type?: unknown } | undefined)?.type === "web_fetch_result" : hits) ? "completed" : "failed",
1389
+ sources: (hits ?? []).flatMap(h => typeof h.url === "string"
1390
+ ? [{ url: h.url, ...(typeof h.title === "string" ? { title: h.title } : {}) }] : []),
1391
+ anthropic: {
1392
+ use: { type: "server_tool_use", id: block.tool_use_id, name: fetch ? "web_fetch" : "web_search", input },
1393
+ result: block as Record<string, unknown>,
1394
+ },
1395
+ };
1396
+ serverSearch = undefined;
1397
+ }
1291
1398
  if (block.type === "redacted_thinking" && typeof block.data === "string") {
1292
1399
  // Opaque redacted block: replay verbatim later or tool-use turns 400.
1293
1400
  yield { type: "redacted_thinking", data: block.data };
@@ -1331,10 +1438,15 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1331
1438
  throw error;
1332
1439
  }
1333
1440
  yield { type: "tool_call_delta", arguments: delta.partial_json };
1441
+ } else if (delta.type === "input_json_delta" && typeof delta.partial_json === "string" && serverSearch?.streaming
1442
+ && serverSearch.json.length < 4096) {
1443
+ // Capped: input that cannot be recovered drops its exchange instead of inventing a call.
1444
+ serverSearch.json += delta.partial_json.slice(0, 4096 - serverSearch.json.length);
1334
1445
  }
1335
1446
  break;
1336
1447
  }
1337
1448
  case "content_block_stop": {
1449
+ if (serverSearch) serverSearch.streaming = false;
1338
1450
  if (currentBlockType === "tool_use") {
1339
1451
  // The non-stream path repairs an unparseable payload in toolUseArguments(); the
1340
1452
  // stream cannot, because the fragments are already downstream. Fail the turn
@@ -1376,8 +1488,13 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1376
1488
  break;
1377
1489
  }
1378
1490
  case "error": {
1379
- const err = data.error as { message?: string } | undefined;
1380
- yield { type: "error", message: err?.message ?? "Anthropic error" };
1491
+ const err = data.error as { message?: unknown; type?: unknown; code?: unknown; status?: unknown } | undefined;
1492
+ yield { type: "error", message: typeof err?.message === "string" ? err.message : "Anthropic error",
1493
+ ...(typeof err?.type === "string" ? { errorType: err.type } : {}),
1494
+ ...(typeof err?.code === "string" ? { code: err.code } : {}),
1495
+ ...(typeof err?.status === "number" && Number.isInteger(err.status) && err.status >= 400 && err.status <= 599
1496
+ ? { status: err.status } : {}),
1497
+ };
1381
1498
  return;
1382
1499
  }
1383
1500
  }
@@ -1396,6 +1513,7 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1396
1513
  // one-terminal-event contract for consumers that keep draining the generator.
1397
1514
  return;
1398
1515
  } finally {
1516
+ replayCapture?.discard();
1399
1517
  if (currentToolCallId) budget.closeCall(currentToolCallId);
1400
1518
  }
1401
1519
  if (!emittedDone) {
@@ -1418,7 +1536,7 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1418
1536
  }
1419
1537
  // Same rule as emitDone: refusal / content_filter is a permanent decision, not a
1420
1538
  // disconnect. This branch bypasses emitDone, so the check has to be repeated here
1421
- // or the EOF route still emits `done` and Codex retries the refusal (#4312).
1539
+ // or the EOF route emits a verdict-less `done` the bridge cannot make terminal (#4312).
1422
1540
  if (isAnthropicContentFilterStopReason(pendingStopReason)) {
1423
1541
  emittedDone = true;
1424
1542
  yield anthropicContentFilterIncomplete(pendingStopReason, usageFromAnthropic(pendingUsage));
@@ -1570,5 +1688,5 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
1570
1688
  }
1571
1689
  },
1572
1690
 
1573
- };
1691
+ }, () => progressUpdates);
1574
1692
  }
@@ -22,6 +22,8 @@ export interface IncomingMeta {
22
22
  * the same pacing queue and custom provider fetch seam.
23
23
  */
24
24
  providerFetch?: typeof globalThis.fetch;
25
+ /** Synchronous selection fence for adapter-owned sends; call after async setup, before dispatch. */
26
+ beforePhysicalSend?: () => void;
25
27
  /**
26
28
  * Image-normalization ladder bias for upstream-413 tightened retries: every image
27
29
  * starts one tier lower (devlog/260714_image_normalization_pipeline/030). Consumed by