@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
@@ -0,0 +1,97 @@
1
+ import type { IncomingMeta, ProviderAdapter } from "./base";
2
+ import { createAnthropicAdapter } from "./anthropic";
3
+ import type { AdapterFactoryContext } from "./registry";
4
+ import type { OcxParsedRequest, OcxProviderConfig } from "../types";
5
+ import { resolveDshAccountMessagesUrl } from "../providers/dsh-account-destination";
6
+
7
+ export { DSH_ACCOUNT_DEFAULT_MESSAGES_URL, resolveDshAccountMessagesUrl } from "../providers/dsh-account-destination";
8
+
9
+ export const DSH_ACCOUNT_RESERVED_HEADERS = new Set([
10
+ "x-dsh-auth-token",
11
+ "anthropic-version",
12
+ "authorization",
13
+ "x-api-key",
14
+ "host",
15
+ ]);
16
+
17
+ export function createDshAccountAdapter(
18
+ provider: OcxProviderConfig,
19
+ _context?: AdapterFactoryContext,
20
+ ): ProviderAdapter {
21
+ // Use key authMode internally so that createAnthropicAdapter doesn't inject Claude Code headers/prompt.
22
+ const baseAdapter = createAnthropicAdapter({
23
+ ...provider,
24
+ authMode: "key",
25
+ }, "none");
26
+
27
+ return {
28
+ ...baseAdapter,
29
+ name: "dsh-account",
30
+
31
+ formatErrorBody(status: number, headers: Headers, payloadText: string): string {
32
+ if (status === 401) {
33
+ return "DeepSeek Harness account authentication failed (401 / ACCOUNT_TOKEN_INVALID). Please sign in to DeepSeek Harness Desktop and re-import.";
34
+ }
35
+ return baseAdapter.formatErrorBody?.(status, headers, payloadText) ?? payloadText;
36
+ },
37
+
38
+ async buildRequest(parsed: OcxParsedRequest, incoming: IncomingMeta) {
39
+ const token = provider.apiKey?.trim();
40
+ if (!token) {
41
+ throw new Error("DeepSeek Harness account token missing — import your account in Providers > DeepSeek Account (DSH)");
42
+ }
43
+
44
+ // Fail-closed if custom provider configuration attempts to set any reserved header
45
+ if (provider.headers) {
46
+ for (const key of Object.keys(provider.headers)) {
47
+ if (DSH_ACCOUNT_RESERVED_HEADERS.has(key.toLowerCase())) {
48
+ throw new Error(`Reserved header "${key}" cannot be overridden in dsh-account provider configuration`);
49
+ }
50
+ }
51
+ }
52
+
53
+ const req = await baseAdapter.buildRequest(parsed, incoming);
54
+ const url = resolveDshAccountMessagesUrl(provider.baseUrl);
55
+
56
+ const headers: Record<string, string> = { ...req.headers };
57
+
58
+ // Apply custom provider headers strictly before authoritative authentication headers
59
+ if (provider.headers) {
60
+ for (const [key, value] of Object.entries(provider.headers)) {
61
+ if (!DSH_ACCOUNT_RESERVED_HEADERS.has(key.toLowerCase()) && typeof value === "string") {
62
+ headers[key] = value;
63
+ }
64
+ }
65
+ }
66
+
67
+ // Authoritative cleanup: strip any API key or Claude headers
68
+ for (const key of Object.keys(headers)) {
69
+ const lower = key.toLowerCase();
70
+ if (
71
+ lower === "authorization"
72
+ || lower === "x-api-key"
73
+ || lower === "x-dsh-auth-token"
74
+ || lower === "anthropic-version"
75
+ || lower === "anthropic-beta"
76
+ || lower === "x-claude-code-session-id"
77
+ || lower === "x-client-request-id"
78
+ ) {
79
+ delete headers[key];
80
+ }
81
+ }
82
+
83
+ // Set authoritative DeepSeek Harness Account headers strictly at the end
84
+ headers["x-dsh-auth-token"] = token;
85
+ headers["anthropic-version"] = "2023-06-01";
86
+ if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
87
+ if (!headers["Accept"]) headers["Accept"] = parsed.stream ? "text/event-stream" : "application/json";
88
+
89
+ return {
90
+ ...req,
91
+ url,
92
+ headers,
93
+ redirect: "manual",
94
+ };
95
+ },
96
+ };
97
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Gemini progress-text history trimming.
3
+ *
4
+ * Gemini models, observed on Gemini 4 Argon over Cloud Code Assist, sometimes skip their thinking
5
+ * channel on a tool-calling turn and plan in the visible text instead. The provider sends that
6
+ * planning as an ordinary text part without a thought marker, for example a progress line in the
7
+ * user's language followed by "Let's read google.ts 580-1700 first." Once one turn like that is
8
+ * replayed as history, the model copies the pattern on the turns that follow, so the leak grows
9
+ * through the rest of the session. Replaying the same 300K-token request with the leaked tails
10
+ * removed from the history stopped the leak, while stronger system-prompt wording did not.
11
+ *
12
+ * The fix is on the history side only. Text that precedes the tool call of a replayed model turn is
13
+ * intermediate progress, and the Google brevity instruction already asks for a single short line
14
+ * there, so it is replayed as its lead sentence. A turn Codex marks `final_answer` and text after a
15
+ * tool call are replayed whole. Visible output is never rewritten, final
16
+ * answers (turns without tool calls) are replayed in full, and the split is punctuation-based so
17
+ * it works the same in every language without matching any wording.
18
+ */
19
+
20
+ /**
21
+ * Sentence end: ASCII terminal punctuation followed by whitespace or the end (so "google.ts" and
22
+ * "3.5" are not boundaries), a script's own sentence terminator (CJK 。!?, Devanagari
23
+ * ।॥, Arabic ۔؟, Armenian ։, Ethiopic ።, Myanmar ။), or a line break. Closing quotes, brackets and
24
+ * Markdown emphasis right after the punctuation belong to the sentence.
25
+ */
26
+ const SENTENCE_END = /[.!?]["'\u201D\u2019)\]*_`]*(?=\s|$)|[\u3002\uFF01\uFF1F\u0964\u0965\u06D4\u061F\u0589\u1362\u104B]["'\u201D\u2019\u300D\u300F)\]*_`]*|\n/g;
27
+
28
+ /** A boundary that only closes a leading list marker ("1.", "a.") or a common abbreviation ("e.g."). */
29
+ const NOT_A_SENTENCE = /^(?:\d{1,3}|[a-z])\.$|(?:^|\s)(?:e\.g|i\.e|etc|vs)\.$/i;
30
+
31
+ /** Whether replayed progress text for this model is trimmed to its lead sentence. */
32
+ export function trimsGeminiProgressHistory(modelId: string): boolean {
33
+ return /gemini/i.test(modelId);
34
+ }
35
+
36
+ /**
37
+ * The lead sentence of a replayed progress text, or undefined when nothing usable remains.
38
+ * Text without a sentence boundary is already a single sentence and is returned whole.
39
+ */
40
+ export function geminiProgressLead(text: string): string | undefined {
41
+ const trimmed = text.trim();
42
+ if (trimmed.length === 0) return undefined;
43
+ SENTENCE_END.lastIndex = 0;
44
+ let end = trimmed.length;
45
+ for (let match = SENTENCE_END.exec(trimmed); match; match = SENTENCE_END.exec(trimmed)) {
46
+ const boundary = match.index + match[0].length;
47
+ if (!NOT_A_SENTENCE.test(trimmed.slice(0, boundary).trim())) { end = boundary; break; }
48
+ }
49
+ const lead = trimmed.slice(0, end).trim();
50
+ // A code-fence opener is not a sentence; replaying it alone would leave an unclosed fence.
51
+ if (lead.length === 0 || lead.startsWith("```")) return undefined;
52
+ return lead;
53
+ }
@@ -1,6 +1,8 @@
1
+ import { openCodeZenGeminiEndpoint } from "../types/opencode-zen";
1
2
  import type { AdapterFetchContext, AdapterRequest, ProviderAdapter } from "./base";
2
3
  import { debugDroppedFrame, debugProviderDiagnosticLazy } from "../lib/debug";
3
4
  import { createToolCallIdAllocator } from "./tool-call-id";
5
+ import { openCodeGoGeminiEndpoint } from "../types/opencode-go";
4
6
  import { createImageBudget, materializeInlineImage, MAX_ENCODED_BYTES_PER_IMAGE, artifactHttpUrl } from "../images/artifacts";
5
7
  import type {
6
8
  AdapterEvent,
@@ -17,7 +19,7 @@ import type {
17
19
  import { isAllowedToolChoice, namespacedToolName, resolveToolChoiceWireName, toolChoiceToolPredicate } from "../types";
18
20
  import type { OcxTool } from "../types";
19
21
  import { contentPartsToText, parseDataUrl } from "./image";
20
- import { getVertexAccessToken } from "../lib/gcp-adc";
22
+ import { GCP_CREDENTIAL_MARKER_PREFIX, gcpCredentialMarkerAccount, getVertexAccessToken } from "../lib/gcp-adc";
21
23
  import { fetchAntigravityWithRetry, fetchVertexWithRetry } from "./google-http";
22
24
  import { safeAntigravityHttpErrorMessage, safeVertexHttpErrorMessage } from "./google-errors";
23
25
  import { isVertexTruncatedTurn, vertexTruncationErrorEvent } from "./google-truncation";
@@ -42,6 +44,7 @@ import {
42
44
  type TranslatorBudget,
43
45
  } from "../lib/translator-budget";
44
46
  import { buildNonOpenAIToolCatalogNudgeForTools } from "./tool-catalog-nudge";
47
+ import { geminiProgressLead, trimsGeminiProgressHistory } from "./google-progress-history";
45
48
  import { configuredReasoningEfforts, mapReasoningEffort } from "../reasoning-effort";
46
49
 
47
50
  // Google-family models (Gemini/Vertex/Antigravity) tend to emit long running commentary between
@@ -174,7 +177,15 @@ function resolveDirectGeminiWireModelId(modelId: string, applyRenames: boolean):
174
177
 
175
178
  /** Vertex API key: provider.apiKey if it looks real (not a sentinel), else GOOGLE_CLOUD_API_KEY env. */
176
179
  function resolveVertexApiKey(optKey?: string): string | undefined {
177
- const realKey = optKey && !optKey.startsWith("<") && optKey !== "N/A" ? optKey : undefined;
180
+ // A `gcp-sa:` marker is the keychain-backed credential reference (see gcp-adc): it is not
181
+ // API-key material, so it must not be sent as x-goog-api-key — the ADC branch below handles it.
182
+ // AND the marker means the operator named one credential explicitly: the GOOGLE_CLOUD_API_KEY
183
+ // env fallback must NOT fire (it would silently route this provider's inference through the
184
+ // ambient env key instead of the configured service account).
185
+ if (optKey && optKey.startsWith(GCP_CREDENTIAL_MARKER_PREFIX)) return undefined;
186
+ const realKey = optKey && !optKey.startsWith("<") && optKey !== "N/A"
187
+ ? optKey
188
+ : undefined;
178
189
  return realKey || process.env.GOOGLE_CLOUD_API_KEY;
179
190
  }
180
191
 
@@ -456,11 +467,26 @@ function messagesToGeminiFormat(
456
467
  const aMsg = msg as OcxAssistantMessage;
457
468
  const parts: unknown[] = [];
458
469
  const toolCalls: Array<{ wireCallId: string; wireName: string }> = [];
470
+ // Progress text before a Gemini tool call replays as its lead sentence, so planning the
471
+ // model leaked into visible text is not copied forward (google-progress-history.ts).
472
+ const trimProgress = trimsGeminiProgressHistory(identityModelId)
473
+ && aMsg.phase !== "final_answer"
474
+ && aMsg.content.some(part => part.type === "toolCall");
475
+ let progressLeadKept = false;
476
+ let afterToolCall = false;
459
477
  for (const p of aMsg.content) {
460
478
  if (p.type === "text") {
461
- const textPart = geminiTextPart((p as OcxTextContent).text);
479
+ let text: unknown = (p as OcxTextContent).text;
480
+ if (trimProgress && !afterToolCall) {
481
+ if (progressLeadKept || typeof text !== "string") continue;
482
+ text = geminiProgressLead(text);
483
+ if (text === undefined) continue;
484
+ progressLeadKept = true;
485
+ }
486
+ const textPart = geminiTextPart(text);
462
487
  if (textPart) parts.push(textPart);
463
488
  } else if (p.type === "toolCall") {
489
+ afterToolCall = true;
464
490
  const tc = p as OcxToolCall;
465
491
  // Preserve the thought signature on the function-call part so Antigravity/Gemini-3
466
492
  // reasoning continuity survives history-driven (stateless) turns, not just same-process
@@ -864,6 +890,7 @@ function invalidGoogleShapeEvent(
864
890
  };
865
891
  }
866
892
 
893
+ /** Build Gemini transport and response parsing for AI Studio, Vertex, Cloud Code Assist or Go. */
867
894
  export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapter {
868
895
  const toolSchemaPolicy = provider.googleToolSchemaPolicy ?? "compatible";
869
896
  const toolSchemaProfile = {
@@ -945,6 +972,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
945
972
  }
946
973
  : {}),
947
974
 
975
+ /** Compile an admitted request into the selected Gemini endpoint, body and authentication. */
948
976
  async buildRequest(parsed: OcxParsedRequest) {
949
977
  // Structured-output admission runs FIRST, before messagesToGeminiFormat writes
950
978
  // lastInjectedCallIds/lastReasoningReplayScope: a refused request must not leave
@@ -983,7 +1011,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
983
1011
  mapReasoningEffort(provider, parsed.modelId, parsed.options.reasoning),
984
1012
  provider.baseUrl,
985
1013
  ).wireModelId
986
- : provider.googleMode === "vertex"
1014
+ : provider.googleMode === "vertex" || openCodeZenGeminiEndpoint(provider, parsed.modelId) !== undefined
987
1015
  ? parsed.modelId
988
1016
  : resolveDirectGeminiWireModelId(parsed.modelId, provider.directGeminiWireRenames !== false);
989
1017
  returnsThoughtSummaries = provider.googleMode === "cloud-code-assist"
@@ -1197,16 +1225,31 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
1197
1225
  if (locationError) throw new Error(locationError);
1198
1226
  const host = location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com`;
1199
1227
  const url = `https://${host}/v1/projects/${project}/locations/${location}/publishers/google/models/${parsed.modelId}:${method}${streamParam}`;
1200
- const token = await getVertexAccessToken();
1228
+ // The marker account travels per-request as an argument (never process-global state): a
1229
+ // module-level "active marker" written at routing time would race across concurrent
1230
+ // requests for different providers and could resolve one call with another's credential.
1231
+ const markerAccount = gcpCredentialMarkerAccount(provider.apiKey);
1232
+ const token = await getVertexAccessToken({ markerAccount });
1201
1233
  headers["Authorization"] = `Bearer ${token}`;
1202
1234
  return { url, method: "POST", headers, body: JSON.stringify(compiled.body) };
1203
1235
  }
1204
1236
 
1205
- // ai-studio (default): Generative Language API + x-goog-api-key.
1206
- const url = `${provider.baseUrl}/v1beta/models/${routedModelId}:${method}${streamParam}`;
1237
+ // Go and Zen use separate guarded Gemini endpoints; AI Studio retains /v1beta.
1238
+ const goEndpoint = openCodeGoGeminiEndpoint(provider, routedModelId);
1239
+ const zenEndpoint = openCodeZenGeminiEndpoint(provider, routedModelId);
1240
+ const endpoint = goEndpoint ?? zenEndpoint ?? `${provider.baseUrl}/v1beta/models/${routedModelId}`;
1241
+ const url = `${endpoint}:${method}${streamParam}`;
1207
1242
  const apiKey = provider.apiKey?.trim();
1208
- if (!apiKey) throw new Error("google (AI Studio) requires a non-empty API key");
1209
- headers["x-goog-api-key"] = apiKey;
1243
+ if (!apiKey) throw new Error(`${goEndpoint ? "OpenCode Go" : zenEndpoint ? "OpenCode Zen" : "google (AI Studio)"} requires a non-empty API key`);
1244
+ if (goEndpoint) {
1245
+ // Console inference documents Bearer auth. Send only the selected Go key.
1246
+ for (const name of Object.keys(headers)) {
1247
+ if (["authorization", "x-goog-api-key", "x-api-key"].includes(name.toLowerCase())) delete headers[name];
1248
+ }
1249
+ headers["Authorization"] = `Bearer ${apiKey}`;
1250
+ } else {
1251
+ headers["x-goog-api-key"] = apiKey;
1252
+ }
1210
1253
 
1211
1254
  const compiled = compileGoogleWireBody(body, toolSchemaProfile, toolSchemaPolicy);
1212
1255
  reportToolSchemaLoss(compiled.toolSchemaLossReport);
@@ -7,6 +7,7 @@ import { EMPTY_TOOL_OUTPUT_ANNOTATION, isWhitespaceOnlyTextPartArray } from "../
7
7
  import { identifyRoutedModel } from "../identity";
8
8
  import { buildNonOpenAIToolCatalogNudgeForTools, shouldInjectNonOpenAIToolCatalogNudge } from "../tool-catalog-nudge";
9
9
  import { peekReasoningForCall } from "../../responses/reasoning-replay-cache";
10
+ import { chatReasoningReplayPolicy } from "./reasoning-replay";
10
11
  import type { ClaudeThinkingProjection } from "../../lib/claude-request-projection";
11
12
  import { inlineDocumentDataUrl } from "../../responses/inline-document";
12
13
  import type { OcxAssistantMessage, OcxContentPart, OcxParsedRequest, OcxProviderConfig, OcxTextContent, OcxThinkingContent, OcxToolCall } from "../../types";
@@ -75,7 +76,7 @@ export function openAIChatSerializesThinking(
75
76
  modelId: string,
76
77
  ): ClaudeThinkingProjection {
77
78
  return {
78
- text: modelInList(provider.preserveReasoningContentModels, modelId),
79
+ text: chatReasoningReplayPolicy(provider, modelId).preserve,
79
80
  // The wire has no `signature` field — base64 replay tokens are Anthropic-wire-only.
80
81
  signature: false,
81
82
  // A `redacted_thinking` block is opaque provider data with no Chat representation: the
@@ -267,7 +268,7 @@ export function messagesToChatFormat(parsed: OcxParsedRequest, provider: OcxProv
267
268
  // recorded under every call id — join unique texts only.
268
269
  if (cached.length > 0) {
269
270
  reasoningContent = [...new Set(cached)].join("\n");
270
- } else if (modelInList(provider.requiresReasoningPlaceholderModels ?? provider.preserveReasoningContentModels, parsed.modelId)) {
271
+ } else if (chatReasoningReplayPolicy(provider, parsed.modelId).placeholder) {
271
272
  // Fallback (extends #950 and #1193; fixes #5421): the replay cache is
272
273
  // bounded (64 entries / 256 KiB / 1 h TTL) and always misses on
273
274
  // long sessions, and some thinking/tool rounds carry no recorded
@@ -354,7 +355,7 @@ export function messagesToChatFormat(parsed: OcxParsedRequest, provider: OcxProv
354
355
  const orphanReasoning =
355
356
  cachedReasoning
356
357
  || (wireSerializesThinking
357
- && modelInList(provider.requiresReasoningPlaceholderModels ?? provider.preserveReasoningContentModels, parsed.modelId)
358
+ && chatReasoningReplayPolicy(provider, parsed.modelId).placeholder
358
359
  ? " "
359
360
  : undefined);
360
361
  const orphanReasoningFields: Record<string, unknown> = !orphanReasoning
@@ -14,6 +14,8 @@ import { modelRecordValue } from "../../reasoning-effort";
14
14
  import { modelInList, type OcxProviderConfig } from "../../types";
15
15
  import { chatParallelToolCallsWireValue } from "./parallel-tool-calls";
16
16
  import { applyExplicitChatReasoningWirePolicy } from "./reasoning-wire";
17
+ import { replayNativeChatReasoning } from "./reasoning-replay";
18
+ import type { OcxReasoningReplayScopeRef } from "../../types";
17
19
 
18
20
  const CHAT_PASSTHROUGH_FIELDS = [
19
21
  "audio",
@@ -55,17 +57,19 @@ export function buildOpenAIChatPassthroughRequest(
55
57
  fastPolicy: ResolvedFastPolicy = fastPolicyForModel(provider, modelId, undefined, "chat"),
56
58
  fastMode?: boolean,
57
59
  providerName?: string,
60
+ replayScope?: OcxReasoningReplayScopeRef,
58
61
  ): AdapterRequest {
59
62
  const { url, headers, hasCredential } = openAIChatTransport(provider);
60
63
 
61
64
  const body: Record<string, unknown> = {
62
65
  model: provider.modelSuffixBracketStrip ? stripBracketedModelSuffix(modelId) : modelId,
63
- // The caller's messages are forwarded as they arrived, with one exception: an operator who
66
+ // The caller's messages retain their wire shape. An operator who
64
67
  // recorded that this destination rejects the `developer` role gets that role converted in
65
68
  // place. Verbatim was not neutral there — it sent the role anyway and the turn failed
66
69
  // upstream with a 400 before the model saw it. An unrecorded destination is still verbatim,
67
70
  // and the conversion changes the role of those messages and nothing else, so the position
68
- // of every message and every other field survive unchanged.
71
+ // of every message and every other field survive unchanged. Missing thinking history is
72
+ // repaired below using the same preserve policy as the translated Chat builder.
69
73
  messages: applyExplicitChatDeveloperRole(
70
74
  frameAgentRouterMessages(provider.baseUrl, rawBody.messages),
71
75
  provider,
@@ -75,6 +79,8 @@ export function buildOpenAIChatPassthroughRequest(
75
79
  for (const field of CHAT_PASSTHROUGH_FIELDS) {
76
80
  if (rawBody[field] !== undefined) body[field] = rawBody[field];
77
81
  }
82
+ body.messages = replayNativeChatReasoning(body.messages, provider, modelId, replayScope,
83
+ Array.isArray(rawBody.tools) && rawBody.tools.length > 0);
78
84
  if (protectGlmSummaryBudget(body, provider.baseUrl, body.reasoning_effort)) body.reasoning_effort = "low";
79
85
  const rawEfforts = modelRecordValue(provider.modelReasoningEfforts, modelId) ?? provider.reasoningEfforts;
80
86
  const reasoningDisabled = modelInList(provider.noReasoningModels, modelId) || rawEfforts?.length === 0;
@@ -0,0 +1,52 @@
1
+ import { modelInList, type OcxProviderConfig, type OcxReasoningReplayScopeRef } from "../../types";
2
+ import { peekReasoningForCall } from "../../responses/reasoning-replay-cache";
3
+
4
+ type Rec = Record<string, unknown>;
5
+ export const isChatRecord = (value: unknown): value is Rec =>
6
+ value !== null && typeof value === "object" && !Array.isArray(value);
7
+
8
+ const isDeepSeekThinkingModel = (modelId: string): boolean =>
9
+ /(?:^|\/)deepseek-(?:v\d+(?:\.\d+)*-(?:flash|pro)|flash|pro)(?:$|[-_:])/i.test(modelId);
10
+
11
+ /** DeepSeek thinking tool replay is a model contract, independent of the gateway's name. */
12
+ export function chatReasoningReplayPolicy(provider: OcxProviderConfig, modelId: string): {
13
+ preserve: boolean; placeholder: boolean;
14
+ } {
15
+ // Match whole model-family tokens, including dotted versions, namespace and deployment suffixes.
16
+ // Legacy chat/reasoner and distilled R1 models do not share the thinking tool-call contract.
17
+ const preserve = provider.preserveReasoningContentModels === undefined
18
+ ? isDeepSeekThinkingModel(modelId)
19
+ : modelInList(provider.preserveReasoningContentModels, modelId);
20
+ return {
21
+ preserve,
22
+ placeholder: preserve && (provider.requiresReasoningPlaceholderModels === undefined
23
+ ? true
24
+ : modelInList(provider.requiresReasoningPlaceholderModels, modelId)),
25
+ };
26
+ }
27
+
28
+ /** Repair missing native history without mutating caller bodies used by retries/combo siblings. */
29
+ export function replayNativeChatReasoning(
30
+ messages: unknown,
31
+ provider: OcxProviderConfig,
32
+ modelId: string,
33
+ scope?: OcxReasoningReplayScopeRef,
34
+ hasTools = false,
35
+ ): unknown {
36
+ const policy = chatReasoningReplayPolicy(provider, modelId);
37
+ if (!policy.preserve || !Array.isArray(messages)
38
+ || modelInList(provider.reasoningDetailsModels, modelId)) return messages;
39
+ return messages.map(message => {
40
+ if (!isChatRecord(message) || message.role !== "assistant"
41
+ || (typeof message.reasoning_content === "string" && message.reasoning_content.length > 0)) return message;
42
+ const calls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
43
+ // DeepSeek's tools contract covers all prior assistant turns, even plain answers.
44
+ // Those turns have no call id for original replay; only the configured fallback applies.
45
+ if (calls.length === 0 && !(hasTools && isDeepSeekThinkingModel(modelId))) return message;
46
+ const cached = calls.filter(isChatRecord)
47
+ .map(tool => typeof tool.id === "string" ? peekReasoningForCall(tool.id, scope) : undefined)
48
+ .filter((text): text is string => typeof text === "string" && text.length > 0);
49
+ const reasoning = cached.length > 0 ? [...new Set(cached)].join("\n") : policy.placeholder ? " " : undefined;
50
+ return reasoning === undefined ? message : { ...message, reasoning_content: reasoning };
51
+ });
52
+ }
@@ -5,7 +5,7 @@ export const MAX_SEARCH_NOTE_FIELD_CHARS = 2048;
5
5
  /** Most `queries` or `sources` entries copied from one hosted call. */
6
6
  export const MAX_SEARCH_NOTE_LIST_ENTRIES = 20;
7
7
  /**
8
- * Serialized bytes all notes of one summary request may use, the omission note included. Request
8
+ * Serialized bytes all projected notes of one request may use, the omission note included. Request
9
9
  * body limits are off by default, so the projection carries its own bound.
10
10
  */
11
11
  export const MAX_PROJECTED_SEARCH_NOTE_BYTES = 65_536;
@@ -49,14 +49,19 @@ function noteBytes(note: unknown): number {
49
49
  }
50
50
 
51
51
  function omissionNote(count: number): Record<string, unknown> {
52
- return referenceNote(`${count} further hosted web search actions omitted from this summary request.`);
52
+ return referenceNote(`${count} further hosted web search actions omitted from this request.`);
53
53
  }
54
54
 
55
- /** Request-local projection of one hosted call for a summarizer with no hosted tool declarations. */
55
+ /** Request-local metadata projection for native Codex replay and portable summaries. */
56
56
  export function renderCompactionSearchHistory(item: unknown): unknown {
57
57
  if (!isPlainObject(item) || item.type !== "web_search_call") return item;
58
58
  // Allowlist action metadata, not opaque state, IDs, or unknown provider fields.
59
59
  const metadata: Record<string, unknown> = stringFields(item, ["status"]);
60
+ if (Array.isArray(item.sources)) {
61
+ metadata.sources = firstEntries(item.sources, source => isPlainObject(source) && typeof source.url === "string"
62
+ ? stringFields(source, ["type", "url", "title"])
63
+ : undefined);
64
+ }
60
65
  if (isPlainObject(item.action)) {
61
66
  const action: Record<string, unknown> = stringFields(item.action, ["type", "query", "url", "pattern"]);
62
67
  if (Array.isArray(item.action.queries)) {
@@ -73,13 +78,15 @@ export function renderCompactionSearchHistory(item: unknown): unknown {
73
78
  }
74
79
 
75
80
  /**
76
- * Project every hosted call in a summary request's input, in place order, within
81
+ * Project every hosted call in the selected input, in place order, within
77
82
  * MAX_PROJECTED_SEARCH_NOTE_BYTES. Once the next note would cross the budget less the reserved
78
83
  * omission note, that call and every later hosted call are dropped, and one omission note stands at
79
84
  * the first dropped position. Items other than hosted calls pass through untouched.
80
85
  */
81
- export function renderCompactionSearchHistoryItems(items: readonly unknown[]): unknown[] {
82
- const hostedCount = items.filter(item => isPlainObject(item) && item.type === "web_search_call").length;
86
+ export function renderCompactionSearchHistoryItems(items: readonly unknown[], referencedHostedIds?: ReadonlySet<string>): unknown[] {
87
+ const shouldProject = (item: unknown) => isPlainObject(item) && item.type === "web_search_call"
88
+ && !(typeof item.id === "string" && referencedHostedIds?.has(item.id));
89
+ const hostedCount = items.filter(shouldProject).length;
83
90
  if (hostedCount === 0) return [...items];
84
91
  const reserve = noteBytes(omissionNote(hostedCount));
85
92
  const out: unknown[] = [];
@@ -87,7 +94,7 @@ export function renderCompactionSearchHistoryItems(items: readonly unknown[]): u
87
94
  let omitted = 0;
88
95
  let omissionIndex = -1;
89
96
  for (const item of items) {
90
- if (!isPlainObject(item) || item.type !== "web_search_call") {
97
+ if (!shouldProject(item)) {
91
98
  out.push(item);
92
99
  continue;
93
100
  }