@bitkyc08/opencodex 2.84.0 → 2.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (518) hide show
  1. package/README.md +0 -4
  2. package/bin/ocx.mjs +61 -3
  3. package/gui/dist/assets/App-ZfYbLHkB.js +58 -0
  4. package/gui/dist/assets/App-wT3mXqFz.css +1 -0
  5. package/gui/dist/assets/{Tray-C9TGtwad.js → Tray-DCd-A1az.js} +1 -1
  6. package/gui/dist/assets/index-CfYDMj34.js +94 -0
  7. package/gui/dist/assets/index-DPeB9HpV.css +1 -0
  8. package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
  9. package/gui/dist/index.html +2 -2
  10. package/package.json +1 -2
  11. package/src/adapters/anthropic/beta-allowlist.ts +91 -11
  12. package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
  13. package/src/adapters/anthropic/client-identity.ts +1 -1
  14. package/src/adapters/anthropic/passthrough.ts +16 -3
  15. package/src/adapters/anthropic-progress.ts +84 -0
  16. package/src/adapters/anthropic.ts +108 -21
  17. package/src/adapters/azure.ts +19 -1
  18. package/src/adapters/base.ts +2 -0
  19. package/src/adapters/claude-cli/adapter.ts +111 -23
  20. package/src/adapters/claude-cli/profiles.ts +2 -1
  21. package/src/adapters/claude-cli/stable-replay.ts +108 -0
  22. package/src/adapters/claude-cli/usage-admission.ts +230 -0
  23. package/src/adapters/client-fingerprint.ts +24 -0
  24. package/src/adapters/coding-agent/protocol.ts +1 -1
  25. package/src/adapters/coding-agent/turn.ts +40 -11
  26. package/src/adapters/cursor/arg-codec.ts +17 -5
  27. package/src/adapters/cursor/catalog.ts +8 -0
  28. package/src/adapters/cursor/effort-map.ts +3 -0
  29. package/src/adapters/cursor/freeform-args.ts +75 -0
  30. package/src/adapters/cursor/protobuf-events.ts +36 -23
  31. package/src/adapters/dsh-account.ts +97 -0
  32. package/src/adapters/google-antigravity-wire.ts +22 -1
  33. package/src/adapters/google.ts +37 -10
  34. package/src/adapters/openai-chat/messages.ts +4 -3
  35. package/src/adapters/openai-chat/passthrough.ts +8 -2
  36. package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
  37. package/src/adapters/openai-chat/response-events.ts +59 -1
  38. package/src/adapters/openai-chat.ts +8 -5
  39. package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
  40. package/src/adapters/openai-responses/passthrough.ts +57 -125
  41. package/src/adapters/openai-responses/request-strips.ts +25 -3
  42. package/src/adapters/openai-responses/transport.ts +81 -0
  43. package/src/adapters/qoder/adapter.ts +44 -3
  44. package/src/adapters/registry.ts +5 -0
  45. package/src/adapters/zed.ts +26 -1
  46. package/src/advisor/activity.ts +55 -0
  47. package/src/advisor/budget.ts +62 -0
  48. package/src/advisor/consult.ts +223 -0
  49. package/src/advisor/context.ts +238 -0
  50. package/src/advisor/disclosure.ts +20 -0
  51. package/src/advisor/evidence-tool.ts +21 -0
  52. package/src/advisor/evidence.ts +117 -0
  53. package/src/advisor/runtime.ts +353 -0
  54. package/src/advisor/settings.ts +144 -0
  55. package/src/advisor/state.ts +369 -0
  56. package/src/advisor/synthetic-tool.ts +33 -0
  57. package/src/bridge/errors.ts +24 -1
  58. package/src/bridge/internal.ts +3 -1
  59. package/src/bridge/response-json.ts +18 -3
  60. package/src/bridge/sse.ts +58 -8
  61. package/src/bridge/terminal-refusal.ts +59 -0
  62. package/src/chat/inbound.ts +30 -6
  63. package/src/chat/outbound.ts +5 -0
  64. package/src/claude/desktop-3p-library.ts +72 -7
  65. package/src/claude/desktop-3p.ts +75 -19
  66. package/src/claude/desktop-first-party.ts +1 -1
  67. package/src/claude/desktop-picker-platform.ts +4 -0
  68. package/src/claude/desktop-picker-profile.ts +11 -4
  69. package/src/claude/desktop-picker.ts +15 -8
  70. package/src/claude/desktop-profile.ts +10 -1
  71. package/src/claude/desktop-remote-store-io.ts +5 -3
  72. package/src/claude/desktop-system-proxy.ts +50 -4
  73. package/src/claude/inbound-content-options.ts +11 -0
  74. package/src/claude/inbound.ts +154 -50
  75. package/src/claude/intercept/local-ca.ts +8 -1
  76. package/src/claude/intercept/picker-ca-startup.ts +2 -1
  77. package/src/claude/intercept/picker-runtime.ts +6 -5
  78. package/src/claude/intercept/picker-trust-windows.ts +338 -0
  79. package/src/claude/intercept/picker-trust.ts +19 -0
  80. package/src/claude/intercept/port-policy.ts +30 -0
  81. package/src/claude/intercept/runtime.ts +97 -94
  82. package/src/claude/model-default-settings.ts +154 -0
  83. package/src/claude/model-info.ts +7 -7
  84. package/src/claude/outbound.ts +51 -19
  85. package/src/cli/account-api.ts +1 -0
  86. package/src/cli/account-policy-dto.ts +6 -0
  87. package/src/cli/account-policy.ts +34 -5
  88. package/src/cli/account-preservation.ts +44 -0
  89. package/src/cli/account.ts +8 -2
  90. package/src/cli/advisor.ts +165 -0
  91. package/src/cli/auxiliary-listener-preflight.ts +37 -0
  92. package/src/cli/capabilities-accounts.ts +20 -1
  93. package/src/cli/capabilities-agents-routing.ts +23 -2
  94. package/src/cli/capabilities-base.ts +39 -11
  95. package/src/cli/capabilities-observe-system.ts +63 -3
  96. package/src/cli/capabilities-provider-models.ts +11 -0
  97. package/src/cli/chatgpt-command.ts +35 -14
  98. package/src/cli/claude.ts +46 -6
  99. package/src/cli/codex-cli-update.ts +151 -41
  100. package/src/cli/codex-provider.ts +44 -0
  101. package/src/cli/combo-input.ts +10 -1
  102. package/src/cli/combo-result.ts +1 -1
  103. package/src/cli/combo.ts +2 -1
  104. package/src/cli/cross-home-owner.ts +17 -14
  105. package/src/cli/dispatch.ts +30 -5
  106. package/src/cli/doctor.ts +15 -1
  107. package/src/cli/ensure-desktop-cold-start.ts +382 -0
  108. package/src/cli/export-command.ts +1 -1
  109. package/src/cli/help.ts +4 -2
  110. package/src/cli/index.ts +14 -5
  111. package/src/cli/integration-plan-dto.ts +3 -0
  112. package/src/cli/log-history.ts +172 -0
  113. package/src/cli/models-runtime-subcommands.ts +1 -0
  114. package/src/cli/models-runtime.ts +72 -8
  115. package/src/cli/observe-stream.ts +2 -0
  116. package/src/cli/observe.ts +16 -4
  117. package/src/cli/provider.ts +27 -5
  118. package/src/cli/registry.ts +41 -9
  119. package/src/cli/restart-scope.ts +3 -1
  120. package/src/cli/runtime-api.ts +5 -3
  121. package/src/cli/system-command.ts +40 -8
  122. package/src/cli/uninstall-client-state.ts +32 -0
  123. package/src/cli/usage-compare.ts +309 -0
  124. package/src/cli/usage-model-search.ts +4 -4
  125. package/src/cli/usage-report.ts +2 -2
  126. package/src/cli/v2.ts +1 -1
  127. package/src/client/machine-listener.ts +1 -3
  128. package/src/client/runtime.ts +1 -0
  129. package/src/clients/client-path.ts +51 -0
  130. package/src/clients/config-export/contracts.ts +3 -1
  131. package/src/clients/config-export/model-metadata.ts +3 -2
  132. package/src/clients/config-export/qoder.ts +93 -0
  133. package/src/clients/config-export.ts +35 -48
  134. package/src/clients/gentle-shell-path.ts +87 -0
  135. package/src/codex/account-auto-switch.ts +2 -0
  136. package/src/codex/account-identity.ts +29 -0
  137. package/src/codex/account-label.ts +1 -1
  138. package/src/codex/account-lifecycle.ts +21 -5
  139. package/src/codex/account-preservation.ts +28 -0
  140. package/src/codex/account-use-remaining.ts +23 -0
  141. package/src/codex/app-server-processes.ts +97 -7
  142. package/src/codex/app-server-restart-service.ts +55 -4
  143. package/src/codex/auth-api/account-list.ts +4 -0
  144. package/src/codex/auth-api/account-pause-group.ts +6 -50
  145. package/src/codex/auth-api/account-preservation.ts +57 -0
  146. package/src/codex/auth-api/pool-mode-gate.ts +5 -2
  147. package/src/codex/auth-api/routes.ts +8 -0
  148. package/src/codex/auth-api/use-remaining-route.ts +36 -0
  149. package/src/codex/auth-api.ts +0 -1
  150. package/src/codex/auth-context.ts +81 -7
  151. package/src/codex/autostart-health.ts +4 -0
  152. package/src/codex/catalog/auto-review.ts +20 -3
  153. package/src/codex/catalog/build-entries.ts +19 -2
  154. package/src/codex/catalog/combo-member.ts +14 -5
  155. package/src/codex/catalog/configured-tiers.ts +50 -0
  156. package/src/codex/catalog/effort.ts +4 -1
  157. package/src/codex/catalog/gather-capture.ts +2 -0
  158. package/src/codex/catalog/metadata.ts +11 -9
  159. package/src/codex/catalog/model-hints.ts +42 -19
  160. package/src/codex/catalog/model-visibility.ts +3 -1
  161. package/src/codex/catalog/native-ultrafast.ts +106 -0
  162. package/src/codex/catalog/parsing.ts +35 -86
  163. package/src/codex/catalog/provider-models.ts +40 -34
  164. package/src/codex/catalog/retained-sync.ts +2 -0
  165. package/src/codex/catalog/routed-gather.ts +33 -16
  166. package/src/codex/catalog/vendor-context.ts +82 -0
  167. package/src/codex/cli-install-provenance.ts +105 -17
  168. package/src/codex/cli-update-plan.ts +545 -0
  169. package/src/codex/config-write-lock.ts +63 -4
  170. package/src/codex/convergence.ts +2 -0
  171. package/src/codex/desktop-app/picker-freshness.ts +96 -0
  172. package/src/codex/desktop-app/types.ts +2 -0
  173. package/src/codex/desktop-app/windows.ts +29 -16
  174. package/src/codex/desktop-app-restart.ts +25 -2
  175. package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
  176. package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
  177. package/src/codex/desktop-compatibility/connection-store.ts +87 -0
  178. package/src/codex/desktop-compatibility/installed-build.ts +73 -0
  179. package/src/codex/desktop-compatibility/json-body.ts +29 -0
  180. package/src/codex/desktop-compatibility/native-identity.ts +62 -0
  181. package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
  182. package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
  183. package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
  184. package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
  185. package/src/codex/desktop-compatibility/runtime.ts +182 -0
  186. package/src/codex/desktop-compatibility/service.ts +30 -0
  187. package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
  188. package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
  189. package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
  190. package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
  191. package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
  192. package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
  193. package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
  194. package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
  195. package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
  196. package/src/codex/desktop-compatibility/windows-key-protection.ts +165 -0
  197. package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
  198. package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
  199. package/src/codex/features.ts +85 -45
  200. package/src/codex/history-provider.ts +1 -1
  201. package/src/codex/inject/config-write-section.ts +4 -1
  202. package/src/codex/inject/history-failure.ts +27 -0
  203. package/src/codex/inject/remove.ts +18 -6
  204. package/src/codex/inject/restore.ts +24 -2
  205. package/src/codex/inject/routing-classify.ts +4 -3
  206. package/src/codex/inject.ts +18 -27
  207. package/src/codex/journal.ts +16 -1
  208. package/src/codex/main-account-hard-lock.ts +30 -1
  209. package/src/codex/main-account.ts +99 -15
  210. package/src/codex/native-main-refresh-mode.ts +217 -0
  211. package/src/codex/native-profile-startup.ts +1 -1
  212. package/src/codex/ocx-compaction-history.ts +12 -10
  213. package/src/codex/paths.ts +86 -1
  214. package/src/codex/provider-routing.ts +174 -0
  215. package/src/codex/refresh.ts +2 -2
  216. package/src/codex/routing/health-store.ts +2 -2
  217. package/src/codex/routing/pool-recovery.ts +79 -0
  218. package/src/codex/routing/selection.ts +4 -3
  219. package/src/codex/routing/thread-affinity.ts +1 -1
  220. package/src/codex/shim-diagnostics.ts +6 -0
  221. package/src/codex/shim-overlay.ts +7 -2
  222. package/src/codex/signed-in-identity.ts +43 -0
  223. package/src/codex/subagent-model-fallback.ts +1 -1
  224. package/src/codex/sync.ts +6 -1
  225. package/src/codex/user-identity.ts +16 -5
  226. package/src/codex/windows-local-appdata.ts +84 -0
  227. package/src/combos/failover.ts +6 -1
  228. package/src/combos/index.ts +1 -0
  229. package/src/combos/resolve.ts +46 -10
  230. package/src/combos/types.ts +15 -0
  231. package/src/config/diagnostics.ts +35 -7
  232. package/src/config/live-reconcile.ts +51 -2
  233. package/src/config/load-degrade.ts +23 -1
  234. package/src/config/opencode-wire-upgrade.ts +54 -0
  235. package/src/config/opencode-zen-upgrade.ts +52 -0
  236. package/src/config/owner-registry.ts +22 -17
  237. package/src/config/paths.ts +10 -3
  238. package/src/config/provider-validation.ts +45 -2
  239. package/src/config/schema/config-schema.ts +39 -0
  240. package/src/config/schema/desktop-compatibility.ts +9 -0
  241. package/src/config/schema/leaf-validators.ts +39 -9
  242. package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
  243. package/src/config/schema/native-reasoning-retention.ts +26 -0
  244. package/src/config.ts +2 -0
  245. package/src/generated/compatibility-version.json +962 -350
  246. package/src/generated/model-metadata.ts +1 -1
  247. package/src/grok/sync.ts +1 -1
  248. package/src/images/loop.ts +46 -8
  249. package/src/integrations/catalog-refresh.ts +1 -1
  250. package/src/integrations/config-io.ts +20 -2
  251. package/src/integrations/journal.ts +15 -1
  252. package/src/integrations/mutation-plan.ts +2 -0
  253. package/src/integrations/registry.ts +15 -0
  254. package/src/integrations/store.ts +3 -0
  255. package/src/integrations/writer.ts +10 -1
  256. package/src/lib/advisor-activation.ts +16 -0
  257. package/src/lib/background-work.ts +65 -0
  258. package/src/lib/bounded-file-read.ts +58 -0
  259. package/src/lib/bun-path-runtime.d.mts +10 -1
  260. package/src/lib/bun-path-runtime.mjs +62 -3
  261. package/src/lib/codex-restart-contract.ts +15 -0
  262. package/src/lib/desktop-proxy-route.ts +30 -0
  263. package/src/lib/desktop-upstream-tunnel.ts +72 -0
  264. package/src/lib/empty-tool-catalog.ts +62 -0
  265. package/src/lib/errors.ts +26 -3
  266. package/src/lib/gcp-adc.ts +96 -9
  267. package/src/lib/gcp-credential-input.ts +12 -0
  268. package/src/lib/lab-live-route-production.ts +5 -0
  269. package/src/lib/local-internal-call-capability.ts +55 -0
  270. package/src/lib/opaque-proxy-tunnel.ts +217 -0
  271. package/src/lib/package-tree-integrity.ts +2 -2
  272. package/src/lib/redact.ts +2 -2
  273. package/src/lib/socks5-fetch.ts +48 -71
  274. package/src/lib/socks5-handshake.ts +112 -0
  275. package/src/lib/translator-budget.ts +7 -1
  276. package/src/oauth/account-quota-rank.ts +69 -2
  277. package/src/oauth/anthropic-account-refusal.ts +20 -2
  278. package/src/oauth/anthropic-oauth-definitions.ts +3 -1
  279. package/src/oauth/anthropic-routing.ts +1 -21
  280. package/src/oauth/anthropic.ts +38 -7
  281. package/src/oauth/callback-server.ts +27 -1
  282. package/src/oauth/chatgpt-device.ts +1 -1
  283. package/src/oauth/chatgpt-shared.ts +83 -0
  284. package/src/oauth/chatgpt.ts +8 -72
  285. package/src/oauth/dsh.ts +203 -0
  286. package/src/oauth/generic-account-failover.ts +62 -12
  287. package/src/oauth/generic-session-affinity.ts +125 -0
  288. package/src/oauth/index.ts +188 -111
  289. package/src/oauth/kiro-device-login.ts +2 -1
  290. package/src/oauth/kiro.ts +7 -1
  291. package/src/oauth/local-token-detect.ts +21 -1
  292. package/src/oauth/login-cli-errors.ts +44 -0
  293. package/src/oauth/login-cli.ts +69 -7
  294. package/src/oauth/login-flow-state.ts +371 -23
  295. package/src/oauth/meta-muse-errors.ts +41 -0
  296. package/src/oauth/meta-muse-keychain.ts +52 -0
  297. package/src/oauth/meta-muse.ts +51 -84
  298. package/src/oauth/model-discovery-auth.ts +3 -0
  299. package/src/oauth/pool-settings-capability.ts +4 -2
  300. package/src/oauth/refresh-flight-retirement.ts +48 -0
  301. package/src/oauth/session-affinity-key.ts +23 -0
  302. package/src/oauth/store-anthropic-instance.ts +9 -2
  303. package/src/oauth/store.ts +12 -5
  304. package/src/protocols/codecs/chat-ir.ts +467 -0
  305. package/src/protocols/codecs/chat.ts +10 -1
  306. package/src/protocols/codecs/messages-ir.ts +500 -0
  307. package/src/protocols/encoders/adapter-events.ts +5 -4
  308. package/src/protocols/encoders/messages.ts +9 -6
  309. package/src/protocols/features.ts +10 -2
  310. package/src/protocols/path.ts +11 -1
  311. package/src/protocols/plan-snapshot.ts +39 -16
  312. package/src/protocols/plan.ts +9 -1
  313. package/src/protocols/provider-summary.ts +2 -1
  314. package/src/protocols/settings.ts +8 -0
  315. package/src/providers/antigravity-account-roster.ts +102 -0
  316. package/src/providers/antigravity-models.ts +85 -7
  317. package/src/providers/api-key-cooldowns.ts +49 -0
  318. package/src/providers/api-key-resolve.ts +11 -6
  319. package/src/providers/api-key-response-ownership.ts +57 -0
  320. package/src/providers/api-key-selection.ts +31 -6
  321. package/src/providers/api-keys.ts +214 -3
  322. package/src/providers/claude-cli-usage.ts +96 -0
  323. package/src/providers/dsh-account-destination.ts +31 -0
  324. package/src/providers/dsh-account-models.ts +41 -0
  325. package/src/providers/gcp-credential-cleanup.ts +59 -0
  326. package/src/providers/key-failover.ts +54 -50
  327. package/src/providers/key-store.ts +7 -2
  328. package/src/providers/model-rename-fields.ts +5 -0
  329. package/src/providers/model-rename-migration.ts +7 -5
  330. package/src/providers/openai-tiers-destination.ts +11 -2
  331. package/src/providers/openai-tiers.ts +5 -12
  332. package/src/providers/opencode-go-transport.ts +3 -1
  333. package/src/providers/quota/account-cache.ts +6 -5
  334. package/src/providers/quota/account-row.ts +34 -0
  335. package/src/providers/quota/antigravity.ts +28 -7
  336. package/src/providers/quota/dsh-account.ts +70 -0
  337. package/src/providers/quota/vendor-probes-oauth.ts +1 -93
  338. package/src/providers/quota-types.ts +2 -0
  339. package/src/providers/quota.ts +14 -20
  340. package/src/providers/registry/entries-core.ts +44 -14
  341. package/src/providers/registry/entries-extended.ts +19 -0
  342. package/src/providers/registry/types.ts +1 -1
  343. package/src/providers/request-pacing.ts +77 -19
  344. package/src/providers/routed-config.ts +339 -0
  345. package/src/providers/service-tier.ts +2 -1
  346. package/src/quota/window-mapping.ts +5 -2
  347. package/src/responses/anthropic-progress.ts +34 -0
  348. package/src/responses/assistant-replay.ts +35 -0
  349. package/src/responses/compaction-history-framing.ts +65 -0
  350. package/src/responses/compaction.ts +6 -1
  351. package/src/responses/incomplete-reason.ts +31 -0
  352. package/src/responses/parser.ts +78 -22
  353. package/src/responses/reasoning-envelope.ts +4 -0
  354. package/src/responses/reasoning-replay-cache.ts +24 -2
  355. package/src/responses/reasoning-retention.ts +173 -0
  356. package/src/responses/retained-compaction.ts +46 -0
  357. package/src/responses/schema.ts +23 -0
  358. package/src/responses/state.ts +32 -29
  359. package/src/router.ts +8 -321
  360. package/src/routing/health.ts +37 -22
  361. package/src/routing/history/build-file.ts +92 -0
  362. package/src/routing/history/indexer.ts +254 -221
  363. package/src/routing/history/ingest.ts +242 -0
  364. package/src/routing/history/rebuild-job.ts +111 -0
  365. package/src/routing/history/rebuild-protocol.ts +15 -0
  366. package/src/routing/history/rebuild-worker.ts +34 -0
  367. package/src/routing/profile.ts +1 -1
  368. package/src/server/admission-model-scope.ts +21 -1
  369. package/src/server/antigravity-direct-eligibility.ts +208 -0
  370. package/src/server/auth-cors.ts +15 -0
  371. package/src/server/chat-completions.ts +82 -10
  372. package/src/server/chat-native-reasoning.ts +123 -0
  373. package/src/server/chat-native-sse.ts +110 -7
  374. package/src/server/chat-native.ts +76 -17
  375. package/src/server/claude-messages.ts +164 -433
  376. package/src/server/effort-policy.ts +1 -1
  377. package/src/server/images.ts +15 -2
  378. package/src/server/index/desktop-compatibility-startup.ts +56 -0
  379. package/src/server/index/model-alias-id.ts +36 -0
  380. package/src/server/index/optional-listeners.ts +2 -0
  381. package/src/server/index/serve-options.ts +11 -3
  382. package/src/server/index/startup-warnings.ts +11 -0
  383. package/src/server/index/websocket-handler.ts +9 -0
  384. package/src/server/index.ts +14 -11
  385. package/src/server/inference/advisor-diagnostics.ts +31 -0
  386. package/src/server/inference/client-encoder-delivery.ts +6 -5
  387. package/src/server/login-shell-path.ts +146 -0
  388. package/src/server/management/advisor-routes.ts +202 -0
  389. package/src/server/management/agent-settings-routes.ts +22 -4
  390. package/src/server/management/claude-desktop-picker-routes.ts +1 -1
  391. package/src/server/management/combo-routes.ts +176 -50
  392. package/src/server/management/companion-routes.ts +4 -0
  393. package/src/server/management/config-routes.ts +77 -6
  394. package/src/server/management/context.ts +4 -0
  395. package/src/server/management/cursor-integration-routes.ts +1 -1
  396. package/src/server/management/desktop-compatibility-routes.ts +46 -0
  397. package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
  398. package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
  399. package/src/server/management/model-routes.ts +13 -8
  400. package/src/server/management/model-rows.ts +14 -0
  401. package/src/server/management/native-integration-routes.ts +2 -7
  402. package/src/server/management/native-reasoning-retention-routes.ts +41 -0
  403. package/src/server/management/oauth-account-routes.ts +87 -14
  404. package/src/server/management/protocol-settings-patch.ts +1 -0
  405. package/src/server/management/provider-gcp-credential-routes.ts +29 -0
  406. package/src/server/management/provider-key-pacing-routes.ts +31 -0
  407. package/src/server/management/provider-overwrite-carry.ts +7 -0
  408. package/src/server/management/provider-routes.ts +25 -30
  409. package/src/server/management/route-registry.ts +21 -1
  410. package/src/server/management/sibling-guard.ts +1 -0
  411. package/src/server/management-api.ts +9 -1
  412. package/src/server/messages-antigravity-direct.ts +195 -0
  413. package/src/server/messages-native-tool-guard.ts +185 -0
  414. package/src/server/messages-native.ts +106 -22
  415. package/src/server/messages-shared.ts +431 -0
  416. package/src/server/relay.ts +1 -1
  417. package/src/server/request-log-terminal-status.ts +1 -1
  418. package/src/server/request-log.ts +13 -1
  419. package/src/server/responses/account-change-state.ts +8 -2
  420. package/src/server/responses/adapter-continuation.ts +48 -19
  421. package/src/server/responses/adapter-delivery.ts +43 -8
  422. package/src/server/responses/adapter-dispatch.ts +48 -4
  423. package/src/server/responses/advisor-plan-slot.ts +27 -0
  424. package/src/server/responses/advisor-retention.ts +117 -0
  425. package/src/server/responses/advisor-slot.ts +368 -0
  426. package/src/server/responses/code-mode-collab-surface.ts +116 -8
  427. package/src/server/responses/codex-ws-wire.ts +15 -8
  428. package/src/server/responses/compact.ts +303 -125
  429. package/src/server/responses/compaction-recovery.ts +18 -10
  430. package/src/server/responses/compaction-retention.ts +61 -0
  431. package/src/server/responses/context-overflow.ts +11 -6
  432. package/src/server/responses/core-auth.ts +6 -0
  433. package/src/server/responses/core-codex-account.ts +11 -1
  434. package/src/server/responses/core-combo-failure.ts +12 -1
  435. package/src/server/responses/core-combo.ts +141 -36
  436. package/src/server/responses/core-errors.ts +3 -3
  437. package/src/server/responses/core-lifetime.ts +7 -2
  438. package/src/server/responses/core-normalize.ts +11 -0
  439. package/src/server/responses/core-opaque-recovery.ts +35 -0
  440. package/src/server/responses/core-options.ts +51 -0
  441. package/src/server/responses/core-pipeline.ts +141 -0
  442. package/src/server/responses/core-replay.ts +25 -6
  443. package/src/server/responses/core.ts +3 -116
  444. package/src/server/responses/fetch-helpers.ts +73 -19
  445. package/src/server/responses/main-hard-lock-routing.ts +109 -0
  446. package/src/server/responses/native-response-control.ts +4 -2
  447. package/src/server/responses/passthrough-delivery.ts +19 -11
  448. package/src/server/responses/passthrough-dispatch.ts +29 -16
  449. package/src/server/responses/request-prepare.ts +144 -17
  450. package/src/server/responses/request-sidecar-auth.ts +2 -2
  451. package/src/server/responses/request-transport.ts +120 -27
  452. package/src/server/responses/response-effects.ts +1 -0
  453. package/src/server/responses/run-turn-execution.ts +166 -38
  454. package/src/server/responses/sidecar-execution.ts +98 -5
  455. package/src/server/responses/terminal-guard.ts +4 -17
  456. package/src/server/responses-undeclared-tool-guard.ts +30 -9
  457. package/src/server/startup-action-control.ts +16 -2
  458. package/src/server/startup-health-cache.ts +1 -0
  459. package/src/service/auth-preflight.ts +71 -0
  460. package/src/service/cli.ts +4 -2
  461. package/src/service/definition.ts +97 -0
  462. package/src/service/desktop-startup.ts +14 -0
  463. package/src/service/guards.ts +1 -61
  464. package/src/service/health-sentinel-linux.ts +112 -0
  465. package/src/service/health-sentinel-main.ts +99 -61
  466. package/src/service/health-sentinel-probe.ts +24 -7
  467. package/src/service/health.ts +5 -89
  468. package/src/service/launchd.ts +27 -7
  469. package/src/service/logging.ts +135 -0
  470. package/src/service/repair.ts +2 -1
  471. package/src/service/state.ts +7 -8
  472. package/src/service/systemd.ts +1 -1
  473. package/src/service/windows-guarded-manager.ts +2 -1
  474. package/src/service/windows-ops.ts +1 -1
  475. package/src/service/windows-scheduler.ts +2 -7
  476. package/src/service/windows-taskxml.ts +2 -2
  477. package/src/service/windows-wscript.ts +14 -0
  478. package/src/service.ts +4 -2
  479. package/src/sidecar/auth.ts +2 -0
  480. package/src/tray/windows-tray.ps1 +43 -24
  481. package/src/types/config.ts +124 -6
  482. package/src/types/opencode-go.ts +61 -0
  483. package/src/types/opencode-zen.ts +29 -0
  484. package/src/types/provider.ts +39 -6
  485. package/src/types/request.ts +38 -2
  486. package/src/types/tools.ts +2 -0
  487. package/src/types/wire.ts +29 -37
  488. package/src/types.ts +1 -0
  489. package/src/update/async-check.ts +1 -1
  490. package/src/update/badge.ts +2 -2
  491. package/src/update/index.ts +88 -15
  492. package/src/update/job-supervised-restart.ts +107 -0
  493. package/src/update/job.ts +32 -32
  494. package/src/update/notify.ts +1 -1
  495. package/src/update/npm-invocation.mjs +12 -2
  496. package/src/update/refresh-scheduler.ts +2 -2
  497. package/src/update/supervised-recovery.d.mts +27 -0
  498. package/src/update/supervised-recovery.mjs +157 -0
  499. package/src/usage/cache-diagnostic.ts +2 -0
  500. package/src/usage/cost.ts +48 -12
  501. package/src/usage/expected-prices.ts +15 -0
  502. package/src/usage/ledger-retention.ts +2 -1
  503. package/src/usage/ledger-scanner.ts +6 -5
  504. package/src/usage/log.ts +17 -8
  505. package/src/usage/summary.ts +22 -9
  506. package/src/usage/user-cost-overlays.ts +14 -19
  507. package/src/usage/user-cost-validation.ts +38 -0
  508. package/src/vision/eligibility.ts +13 -0
  509. package/src/web-search/gemini-executor.ts +2 -2
  510. package/src/web-search/loop.ts +55 -5
  511. package/src/web-search/progress-stream.ts +4 -1
  512. package/src/web-search/run-turn-loop.ts +9 -1
  513. package/assets/sponsors/packycode.png +0 -0
  514. package/gui/dist/assets/App-CSJvmjNs.css +0 -1
  515. package/gui/dist/assets/App-CZtqiGcw.js +0 -56
  516. package/gui/dist/assets/index-BB6uK6tU.css +0 -1
  517. package/gui/dist/assets/index-CTm4l-HQ.js +0 -94
  518. package/gui/dist/assets/quota-summary-BbEeRfPY.js +0 -1
@@ -0,0 +1,368 @@
1
+ import { assistantReplayContent } from "../../responses/assistant-replay";
2
+ /**
3
+ * Core-owned registration slot for the optional advisor subsystem (src/advisor).
4
+ *
5
+ * This file is the ONLY thing the core Responses path knows about the advisor. It holds the
6
+ * structural plan interface and the event-stream guard — pure protocol machinery over
7
+ * src/types — and imports nothing from src/advisor at runtime. The optional subsystem registers
8
+ * a factory through advisor-plan-slot at host activation; an advisor-disabled install therefore executes no advisor
9
+ * code and imports no advisor module (same seam discipline as src/lab).
10
+ *
11
+ * Guard semantics (mirrors guardTerminalEventStream):
12
+ * - synthetic `advisor` tool-call events are HELD — the Codex client never sees the tool, the
13
+ * call, or its arguments;
14
+ * - on a clean `done`, each held call is consulted through the plan, the advice is appended as a
15
+ * paired assistant-toolCall + toolResult message pair, and the worker is re-dispatched via the
16
+ * SAME continuation machinery the terminal guard uses;
17
+ * - real (non-advisor) tool calls end interception for the leg: the turn belongs to the client;
18
+ * - usage from intercepted legs is merged into the final terminal event so worker accounting
19
+ * stays complete; the advisor's own usage is a separate loopback request and never merges here;
20
+ * - consultations and worker continuations have separate hard per-request bounds. Exhaustion
21
+ * removes the tool; one final limit-result continuation is allowed, then a typed error ends it.
22
+ */
23
+ import type {
24
+ AdapterEvent,
25
+ OcxAssistantContentPart,
26
+ OcxAssistantMessage,
27
+ OcxMessage,
28
+ OcxParsedRequest,
29
+ OcxToolResultMessage,
30
+ OcxUsage,
31
+ } from "../../types";
32
+ import { jsonStringPartsUtf8Bytes, jsonUtf8Bytes } from "../../lib/json-byte-size";
33
+ import { isTranslatorBudgetExceededError, TRANSLATOR_MAX_CALL_ARGUMENT_BYTES, TranslatorBudgetExceededError, type TranslatorBudget } from "../../lib/translator-budget";
34
+ import { AdvisorRetention } from "./advisor-retention";
35
+
36
+ export const ADVISOR_TOOL_NAME = "advisor";
37
+
38
+ /** Hard bound on advisor consultations per worker request (recursion guard). */
39
+ export const MAX_ADVISOR_CONSULTATIONS_PER_REQUEST = 3;
40
+ /** At most one final worker continuation after consultation exhaustion. */
41
+ export const MAX_ADVISOR_CONTINUATIONS_PER_REQUEST = MAX_ADVISOR_CONSULTATIONS_PER_REQUEST + 1;
42
+
43
+ /** Per-leg retention caps for rebuilding the assistant message (see terminal-guard's bounded retention). */
44
+ const MAX_LEG_TEXT_CHARS = 16 * 1_024;
45
+ const MAX_LEG_THINKING_CHARS = 16 * 1_024;
46
+
47
+ /** Outcome of one consultation, as the plan reports it to the guard. */
48
+ export interface AdvisorConsultOutcome {
49
+ ok: boolean;
50
+ /** Formatted, wrapper-marked advice text ready to hand to the worker. */
51
+ content: string;
52
+ isError: boolean;
53
+ /** The consultation was cancelled by the caller (client abort) — not a provider failure. */
54
+ cancelled?: boolean;
55
+ /**
56
+ * Operator config blocked the consultation before any outbound call. Not a provider
57
+ * failure: preflight must release a claim instead of recording a cooldown.
58
+ */
59
+ blocked?: "consent" | "settings" | "budget" | "limit";
60
+ }
61
+
62
+ /** The structural plan the optional advisor subsystem registers per request. */
63
+ export interface AdvisorPlan {
64
+ /**
65
+ * Run one consultation for the current conversation state. Implementations own dedup,
66
+ * logging, and usage accounting; the guard only consumes the outcome.
67
+ */
68
+ consult(
69
+ parsed: OcxParsedRequest,
70
+ reason: "manual",
71
+ question: string | undefined,
72
+ ): Promise<AdvisorConsultOutcome>;
73
+ /**
74
+ * Runtime-owned text for a failed or limited consultation. The guard never composes failure
75
+ * prose itself: the implementation neutralizes untrusted text so no guard path can emit
76
+ * something the provenance detector would read as genuine advice.
77
+ */
78
+ formatUnavailable(kind: "manual" | "limit", error: string): string;
79
+ }
80
+
81
+ interface HeldAdvisorCall {
82
+ id: string;
83
+ name: string;
84
+ args: string[];
85
+ argumentBytes: number;
86
+ question?: string;
87
+ closed: boolean;
88
+ providerMetadata?: import("../../types").OcxProviderOpaqueToolCallMetadata;
89
+ }
90
+
91
+ function parseAdvisorArgs(call: HeldAdvisorCall, retention: AdvisorRetention): { question?: string } {
92
+ // Reserve both the joined input and parsed JSON before either is materialized.
93
+ return retention.copy(call.argumentBytes * 2 + 4, () => parseArgs(call.args.join("")));
94
+ }
95
+
96
+ function parseArgs(argsBuf: string): { question?: string } {
97
+ if (argsBuf.trim() === "") return {};
98
+ try {
99
+ const parsed: unknown = JSON.parse(argsBuf);
100
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
101
+ const question = (parsed as { question?: unknown }).question;
102
+ if (typeof question === "string" && question.trim() !== "") return { question: question.slice(0, 2_000) };
103
+ }
104
+ } catch { /* malformed args → no focus question */ }
105
+ return {};
106
+ }
107
+
108
+ function assistantMessageFromLeg(
109
+ events: readonly AdapterEvent[],
110
+ held: readonly HeldAdvisorCall[],
111
+ timestamp: number,
112
+ retention: AdvisorRetention,
113
+ ): OcxAssistantMessage | undefined {
114
+ const content = assistantReplayContent(events);
115
+ for (const call of held) {
116
+ if (!call.closed) continue;
117
+ const args = parseAdvisorArgs(call, retention);
118
+ call.question = args.question;
119
+ content.push({
120
+ type: "toolCall",
121
+ id: call.id,
122
+ name: call.name,
123
+ arguments: args,
124
+ ...(call.providerMetadata ? { providerMetadata: call.providerMetadata } : {}),
125
+ });
126
+ }
127
+ if (content.length === 0) return undefined;
128
+ return { role: "assistant", content, timestamp };
129
+ }
130
+
131
+ function advisorToolResult(
132
+ call: HeldAdvisorCall,
133
+ outcome: AdvisorConsultOutcome,
134
+ timestamp: number,
135
+ ): OcxToolResultMessage {
136
+ return {
137
+ role: "toolResult",
138
+ toolCallId: call.id,
139
+ toolName: ADVISOR_TOOL_NAME,
140
+ content: outcome.content,
141
+ isError: outcome.isError,
142
+ timestamp,
143
+ };
144
+ }
145
+
146
+ export interface AdvisorGuardOptions {
147
+ parsed: OcxParsedRequest;
148
+ plan: AdvisorPlan;
149
+ firstEvents: AsyncIterable<AdapterEvent>;
150
+ /** Independent guard ownership within the optional shared request budget. */
151
+ translatorBudget?: TranslatorBudget;
152
+ /** One bounded worker continuation re-dispatch (same machinery as the terminal guard). */
153
+ continuation: (parsed: OcxParsedRequest) => AsyncIterable<AdapterEvent> | Promise<AsyncIterable<AdapterEvent>>;
154
+ }
155
+
156
+ /**
157
+ * Wrap a worker event stream with advisor interception. Registered on the parsed request as
158
+ * `_advisorGuard` by the sidecar planner; adapter delivery applies it when present.
159
+ */
160
+ export function createAdvisorStreamGuard(options: AdvisorGuardOptions): AsyncGenerator<AdapterEvent> {
161
+ const guard = createAdvisorGuard(options.plan);
162
+ return guard(options);
163
+ }
164
+ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequest["_advisorGuard"]> {
165
+ // Shared across invocations of this request's guard, including an empty-completion retry.
166
+ let consultations = 0;
167
+ let continuations = 0;
168
+ let finalContinuationUsed = false;
169
+ return async function* guardAdvisorStream(options: Omit<AdvisorGuardOptions, "plan">): AsyncGenerator<AdapterEvent> {
170
+ const maxConsultations = MAX_ADVISOR_CONSULTATIONS_PER_REQUEST;
171
+ let parsed = options.parsed;
172
+ let accumulatedUsage: OcxUsage | undefined;
173
+ let source: AsyncIterable<AdapterEvent> = options.firstEvents;
174
+ const retention = new AdvisorRetention(options.translatorBudget);
175
+ try {
176
+ while (true) {
177
+ const held: HeldAdvisorCall[] = [];
178
+ const legEvents: AdapterEvent[] = [];
179
+ let legTextChars = 0;
180
+ let legThinkingChars = 0;
181
+ let pending: HeldAdvisorCall | null = null;
182
+ // While a tool call is open, its delta/end events belong to that call. Advisor calls are
183
+ // held from the output; real calls pass through untouched.
184
+ let holdingCurrent = false;
185
+ let hasRealToolCall = false;
186
+ let terminalConsumed = false;
187
+ let terminalEvent: Extract<AdapterEvent, { type: "done" }> | undefined;
188
+
189
+ for await (const event of source) {
190
+ if (event.type === "tool_call_start") {
191
+ if (pending) { held.push(pending); pending = null; }
192
+ holdingCurrent = event.name === ADVISOR_TOOL_NAME;
193
+ if (!holdingCurrent) {
194
+ hasRealToolCall = true;
195
+ // No continuation can use this leg. Real-tool arguments are never copied.
196
+ held.length = legEvents.length = 0;
197
+ retention.releaseLeg();
198
+ } else {
199
+ if (!hasRealToolCall) {
200
+ retention.retain(event, true);
201
+ pending = { id: event.id, name: event.name, args: [], argumentBytes: 0, closed: false, ...(event.providerMetadata ? { providerMetadata: event.providerMetadata } : {}) };
202
+ }
203
+ continue;
204
+ }
205
+ } else if (event.type === "tool_call_delta") {
206
+ if (pending) {
207
+ const bytes = jsonStringPartsUtf8Bytes([event.arguments], TRANSLATOR_MAX_CALL_ARGUMENT_BYTES + 2) - 2;
208
+ if (bytes > TRANSLATOR_MAX_CALL_ARGUMENT_BYTES - pending.argumentBytes) {
209
+ throw new TranslatorBudgetExceededError("tool_args", TRANSLATOR_MAX_CALL_ARGUMENT_BYTES);
210
+ }
211
+ retention.retain(event);
212
+ pending.argumentBytes += bytes;
213
+ pending.args.push(event.arguments);
214
+ }
215
+ if (holdingCurrent) continue;
216
+ } else if (event.type === "tool_call_end") {
217
+ if (pending) {
218
+ retention.retain(event);
219
+ pending.closed = true;
220
+ if (pending.name === ADVISOR_TOOL_NAME) held.push(pending);
221
+ pending = null;
222
+ }
223
+ if (holdingCurrent) {
224
+ holdingCurrent = false;
225
+ continue;
226
+ }
227
+ } else if (event.type === "done") {
228
+ if (!hasRealToolCall) retention.retain(event);
229
+ if (pending) { held.push(pending); pending = null; }
230
+ terminalEvent = event;
231
+ terminalConsumed = true;
232
+ break;
233
+ } else if (event.type === "incomplete" || event.type === "error") {
234
+ // A broken leg cannot safely anchor a continuation: surface the terminal as-is.
235
+ if (pending) { pending = null; }
236
+ accumulatedUsage = retention.usage(accumulatedUsage, event.usage);
237
+ yield accumulatedUsage ? { ...event, usage: accumulatedUsage } : event;
238
+ return;
239
+ } else {
240
+ // Retain (bounded) the leg's visible content so the rebuilt assistant message is complete.
241
+ if (event.type === "text_delta") {
242
+ legTextChars += event.text.length;
243
+ // Empty deltas add nothing to the rebuilt message; non-empty ones are bounded by the cap.
244
+ if (!hasRealToolCall && event.text !== "" && legTextChars <= MAX_LEG_TEXT_CHARS) { retention.retain(event, false, false); legEvents.push(event); }
245
+ } else if (event.type === "thinking_delta") {
246
+ legThinkingChars += event.thinking.length;
247
+ if (!hasRealToolCall && event.thinking !== "" && legThinkingChars <= MAX_LEG_THINKING_CHARS) { retention.retain(event, false, false); legEvents.push(event); }
248
+ } else if (
249
+ event.type === "thinking_signature"
250
+ || event.type === "redacted_thinking"
251
+ ) {
252
+ if (!hasRealToolCall) { retention.retain(event); legEvents.push(event); }
253
+ }
254
+ }
255
+ yield event;
256
+ }
257
+
258
+ const advisorCalls = held.filter(call => call.name === ADVISOR_TOOL_NAME && call.closed);
259
+ const shouldIntercept = terminalConsumed
260
+ && terminalEvent?.stopReason !== "max_tokens"
261
+ && terminalEvent?.stopReason !== "content_filter"
262
+ && advisorCalls.length > 0
263
+ && !hasRealToolCall;
264
+
265
+ if (!shouldIntercept) {
266
+ // Plain leg: surface the terminal with merged usage from any earlier intercepted legs.
267
+ if (terminalEvent) {
268
+ accumulatedUsage = retention.usage(accumulatedUsage, terminalEvent.usage);
269
+ yield accumulatedUsage ? { ...terminalEvent, usage: accumulatedUsage } : terminalEvent;
270
+ }
271
+ return;
272
+ }
273
+
274
+ // Consume this leg's done — the continuation replaces it.
275
+ accumulatedUsage = retention.usage(accumulatedUsage, terminalEvent?.usage);
276
+ if (continuations >= MAX_ADVISOR_CONTINUATIONS_PER_REQUEST || finalContinuationUsed) {
277
+ yield {
278
+ type: "error",
279
+ status: 502,
280
+ errorType: "advisor_continuation_limit",
281
+ message: "Advisor worker continuation limit reached; no further worker calls were sent.",
282
+ ...(accumulatedUsage ? { usage: accumulatedUsage } : {}),
283
+ };
284
+ return;
285
+ }
286
+ // A repeated call after tool removal gets one last paired limit result, never a loop.
287
+ const isFinalContinuation = consultations >= maxConsultations;
288
+ // Reserve before any consultation await, so simultaneous guard entries share the bound.
289
+ continuations += 1;
290
+ finalContinuationUsed ||= isFinalContinuation;
291
+ const timestamp = Date.now();
292
+ const assistant = retention.history(retention.replayReservation(), () => assistantMessageFromLeg(legEvents, advisorCalls, timestamp, retention));
293
+ const messages: OcxMessage[] = [...parsed.context.messages];
294
+ if (assistant) messages.push(assistant);
295
+
296
+ for (const call of advisorCalls) {
297
+ if (consultations >= maxConsultations) {
298
+ // Runtime-owned limit text: not advice, and never a genuine advice wrapper.
299
+ const outcome = {
300
+ ok: false,
301
+ isError: true,
302
+ content: plan.formatUnavailable("limit", "consultation limit reached for this request"),
303
+ };
304
+ messages.push(retention.history(jsonUtf8Bytes(outcome) + jsonUtf8Bytes(call.id) + 256, () => advisorToolResult(call, outcome, timestamp))!);
305
+ continue;
306
+ }
307
+ consultations += 1;
308
+ // Later calls in the same leg must see what came before them: the rebuilt worker turn
309
+ // and every earlier advisor result in this leg's accumulated messages.
310
+ const consultParsed: OcxParsedRequest = {
311
+ ...parsed,
312
+ context: { ...parsed.context, messages },
313
+ };
314
+ let outcome: AdvisorConsultOutcome;
315
+ try {
316
+ outcome = await plan.consult(consultParsed, "manual", call.question);
317
+ } catch (error) {
318
+ if (isTranslatorBudgetExceededError(error)) throw error;
319
+ // Runtime-owned failure text only: the implementation neutralizes untrusted exception
320
+ // text so it can never forge a genuine advice wrapper.
321
+ outcome = {
322
+ ok: false,
323
+ isError: true,
324
+ content: plan.formatUnavailable("manual", error instanceof Error ? error.message : String(error)),
325
+ };
326
+ }
327
+ messages.push(retention.history(jsonUtf8Bytes(outcome) + jsonUtf8Bytes(call.id) + 256, () => advisorToolResult(call, outcome, timestamp))!);
328
+ }
329
+
330
+ const exhausted = consultations >= maxConsultations;
331
+ const tools = exhausted
332
+ ? parsed.context.tools?.filter(tool => !tool.advisor && tool.name !== ADVISOR_TOOL_NAME)
333
+ : parsed.context.tools;
334
+ const choice = parsed.options.toolChoice;
335
+ const nextParsed: OcxParsedRequest = {
336
+ ...parsed,
337
+ context: { ...parsed.context, messages, tools },
338
+ options: exhausted && ((typeof choice === "object" && choice !== null && "name" in choice && choice.name === ADVISOR_TOOL_NAME)
339
+ || (choice === "required" && !tools?.length))
340
+ ? { ...parsed.options, toolChoice: "auto" }
341
+ : parsed.options,
342
+ };
343
+ parsed = nextParsed;
344
+ // All replay owners were admitted while the raw leg was still live.
345
+ held.length = legEvents.length = advisorCalls.length = 0;
346
+ pending = null;
347
+ terminalEvent = undefined;
348
+ retention.releaseLeg();
349
+ yield { type: "assistant_boundary" };
350
+ try {
351
+ source = await options.continuation(parsed);
352
+ } catch (error) {
353
+ if (isTranslatorBudgetExceededError(error)) throw error;
354
+ yield {
355
+ type: "error",
356
+ message: error instanceof Error ? error.message : String(error),
357
+ ...(accumulatedUsage ? { usage: accumulatedUsage } : {}),
358
+ };
359
+ return;
360
+ }
361
+ }
362
+ } catch (error) {
363
+ if (!isTranslatorBudgetExceededError(error)) throw error;
364
+ yield { type: "error", status: 502, errorType: "upstream_error", code: "translation_buffer_limit",
365
+ message: "upstream translation buffer exceeded the safe limit", ...(accumulatedUsage ? { usage: accumulatedUsage } : {}) };
366
+ } finally { retention.release(); }
367
+ };
368
+ }
@@ -77,13 +77,104 @@ interface RenderedTool { name: string; raw: string }
77
77
  /** The declaration fence codex-rs opens: three backticks and "ts". */
78
78
  const DECLARATION_FENCE_SHAPE: Fence = { char: "`", length: 3 };
79
79
 
80
- /** A complete declaration as render_code_mode_sample emits it, single- or multi-line. */
81
- function isCompleteDeclaration(body: readonly string[], name: string): boolean {
80
+ const DECLARATION_PREFIX = "declare const tools: { ";
81
+ const PROMISE_SEPARATOR = "): Promise<";
82
+ const DECLARATION_SUFFIX = ">; };";
83
+ // Bounds keep a caller-controlled description linear work: a rendered listing is a heading, a
84
+ // description, and a small declaration per tool, so anything this large cannot be the codex-rs
85
+ // shape this parser exists to recognize.
86
+ const MAX_DESCRIPTION_LENGTH = 1 << 20;
87
+ const MAX_DECLARATION_LENGTH = 64 * 1024;
88
+
89
+ const isSpace = (ch: string | undefined): boolean => ch === undefined || /\s/.test(ch);
90
+
91
+ /**
92
+ * The declared tool name when body is a complete declaration as render_code_mode_sample emits it,
93
+ * single- or multi-line, else undefined. Fixed boundaries plus one forward scan that balances the
94
+ * parameter list's brackets and the return type's generics: no backtracking, so a fence body full
95
+ * of candidate separators stays linear work.
96
+ */
97
+ function completeDeclarationName(body: readonly string[]): string | undefined {
82
98
  const text = body.join("\n");
83
- const prefix = "declare const tools: { " + name + "(";
84
- if (!text.startsWith(prefix)) return false;
99
+ if (text.length > MAX_DECLARATION_LENGTH) return undefined;
100
+ if (!text.startsWith(DECLARATION_PREFIX) || !text.endsWith(DECLARATION_SUFFIX)) return undefined;
101
+ const signature = text.slice(DECLARATION_PREFIX.length, -DECLARATION_SUFFIX.length);
102
+ const openParen = signature.indexOf("(");
103
+ const name = openParen > 0 ? signature.slice(0, openParen) : "";
104
+ if (!/^[A-Za-z_$][\w$]*$/.test(name)) return undefined;
85
105
  // A named, typed parameter (`args` for functions, `input` for freeform) and a non-empty return type.
86
- return /^(?:args|input): \S[\s\S]*\): Promise<[^>\s][\s\S]*>; \};$/.test(text.slice(prefix.length));
106
+ const rest = signature.slice(openParen + 1);
107
+ const typed = rest.startsWith("args: ") ? 6 : rest.startsWith("input: ") ? 7 : -1;
108
+ if (typed < 0 || isSpace(rest[typed])) return undefined;
109
+ // Quoted text and comments are opaque to delimiter matching. Each skip advances
110
+ // past its text once; an unterminated region cannot contain a closing declaration.
111
+ const isOpaqueTypeText = (at: number): boolean => {
112
+ const ch = rest[at];
113
+ return ch === '"' || ch === "'" || ch === "`"
114
+ || ch === "/" && (rest[at + 1] === "/" || rest[at + 1] === "*");
115
+ };
116
+ const skipTypeText = (at: number): number | undefined => {
117
+ const quote = rest[at]!;
118
+ if (quote === "/" && rest[at + 1] === "/") {
119
+ let i = at + 2;
120
+ while (i < rest.length && rest[i] !== "\n" && rest[i] !== "\r"
121
+ && rest[i] !== "\u2028" && rest[i] !== "\u2029") i += 1;
122
+ return i < rest.length ? i : undefined;
123
+ }
124
+ if (quote === "/" && rest[at + 1] === "*") {
125
+ const end = rest.indexOf("*/", at + 2);
126
+ return end < 0 ? undefined : end + 1;
127
+ }
128
+ let i = at + 1;
129
+ while (i < rest.length) {
130
+ const ch = rest[i]!;
131
+ if (ch === "\\") i += 2;
132
+ else if (ch === quote) return i;
133
+ else i += 1;
134
+ }
135
+ return undefined;
136
+ };
137
+ // The parameter list ends at the ')' that balances the opening '(': a ')' nested inside the
138
+ // parameter's own type (e.g. `args: (a: string) => Promise<void>`) cannot pose as the separator.
139
+ // The type must also say something before that ')': `args: )` or `args: /* note */)` names no type.
140
+ let depth = 1;
141
+ let close = -1;
142
+ let typeContent = false;
143
+ for (let at = typed; at < rest.length; at += 1) {
144
+ const ch = rest[at]!;
145
+ if (isOpaqueTypeText(at)) {
146
+ if (ch !== "/") typeContent = true; // a quoted literal is a type, a comment is not
147
+ const end = skipTypeText(at);
148
+ if (end === undefined) return undefined;
149
+ at = end;
150
+ continue;
151
+ }
152
+ if (ch === "(" || ch === "[" || ch === "{") depth += 1;
153
+ else if (ch === ")" || ch === "]" || ch === "}") depth -= 1;
154
+ if (depth === 0) { close = at; break; }
155
+ if (!isSpace(ch)) typeContent = true;
156
+ }
157
+ if (close < 0 || !typeContent || !rest.startsWith(PROMISE_SEPARATOR, close)) return undefined;
158
+ const head = rest[close + PROMISE_SEPARATOR.length];
159
+ if (head === ">" || isSpace(head)) return undefined;
160
+ // The return type runs to the stripped suffix, which already supplied exactly one '>' for the
161
+ // Promise's '<': generic depth must stay positive to the end and land at exactly 1 — a '>' that
162
+ // reaches zero ('Promise<unknown>; garbage') is surplus text, a depth left above 1
163
+ // ('Promise<Map<string, unknown') never closed its own generic, and a '=>' arrow never counts.
164
+ let generic = 1;
165
+ for (let at = close + PROMISE_SEPARATOR.length; at < rest.length; at += 1) {
166
+ const ch = rest[at]!;
167
+ if (isOpaqueTypeText(at)) {
168
+ const end = skipTypeText(at);
169
+ if (end === undefined) return undefined;
170
+ at = end;
171
+ continue;
172
+ }
173
+ if (ch === "<") generic += 1;
174
+ else if (ch === ">" && rest[at - 1] !== "=") generic -= 1;
175
+ if (generic === 0) return undefined;
176
+ }
177
+ return generic === 1 ? name : undefined;
87
178
  }
88
179
 
89
180
  /** codex-rs normalize_code_mode_identifier: invalid identifier characters become `_`. */
@@ -114,9 +205,21 @@ function renderedHeading(match: RegExpExecArray): RenderedTool | undefined {
114
205
  * alone. Lines inside a quoted fence can neither open a section nor declare a tool.
115
206
  */
116
207
  function renderedNestedTools(description: string): RenderedTool[] {
208
+ if (description.length > MAX_DESCRIPTION_LENGTH) return [];
117
209
  const lines = description.split("\n");
118
210
  const rendered: RenderedTool[] = [];
119
211
  const pending: RenderedTool[] = [];
212
+ // Ascending indexes into `pending` per name, so a declaration finds its owner in one lookup
213
+ // instead of rescanning every pending heading.
214
+ const pendingByName = new Map<string, number[]>();
215
+ const dropPendingTo = (length: number): void => {
216
+ while (pending.length > length) {
217
+ const removed = pending.pop()!;
218
+ const indexes = pendingByName.get(removed.name)!;
219
+ indexes.pop();
220
+ if (indexes.length === 0) pendingByName.delete(removed.name);
221
+ }
222
+ };
120
223
  let fence: Fence | undefined;
121
224
  for (let i = 0; i < lines.length; i += 1) {
122
225
  const line = lines[i]!;
@@ -127,7 +230,11 @@ function renderedNestedTools(description: string): RenderedTool[] {
127
230
  const match = TOOL_HEADING.exec(line);
128
231
  if (match) {
129
232
  const heading = renderedHeading(match);
130
- if (heading) pending.push(heading);
233
+ if (heading) {
234
+ const indexes = pendingByName.get(heading.name);
235
+ if (indexes) indexes.push(pending.length); else pendingByName.set(heading.name, [pending.length]);
236
+ pending.push(heading);
237
+ }
131
238
  continue;
132
239
  }
133
240
  if (line === DECLARATION_LEAD && lines[i + 1] === DECLARATION_FENCE) {
@@ -137,10 +244,11 @@ function renderedNestedTools(description: string): RenderedTool[] {
137
244
  const body = lines.slice(i + 2, close);
138
245
  // The outermost pending heading with the declared name owns it, so a same-name heading quoted
139
246
  // inside the tool's own description cannot take over the genuine section.
140
- const owner = pending.findIndex(tool => isCompleteDeclaration(body, tool.name));
247
+ const declared = completeDeclarationName(body);
248
+ const owner = declared === undefined ? -1 : pendingByName.get(declared)?.[0] ?? -1;
141
249
  if (owner >= 0) {
142
250
  rendered.push(pending[owner]!);
143
- pending.length = owner;
251
+ dropPendingTo(owner);
144
252
  }
145
253
  i = close;
146
254
  continue;
@@ -1,3 +1,4 @@
1
+ import { normalizeResponsesIncompleteReason } from "../../responses/incomplete-reason";
1
2
  import { MAX_CLIENT_SSE_FRAME_BYTES } from "../sse-frame-buffer";
2
3
  import {
3
4
  isTransientUpstreamStatus,
@@ -8,6 +9,7 @@ import {
8
9
  import { isRateLimitOrQuotaFailureMessage, recognizedUpstreamError } from "../../lib/errors";
9
10
  import type { RequestFailureCause, RequestFailureStage } from "../../lib/request-failure-model";
10
11
  import { packageVersion } from "../../lib/package-version";
12
+ import { registerProviderResponseProvenance } from "../../providers/request-pacing";
11
13
  // If the 101 never arrives (network black hole), give SSE a chance well before
12
14
  // the caller's connect timeout (default 200s) would fire.
13
15
  export const UPGRADE_DEADLINE_MS = 10_000;
@@ -49,9 +51,12 @@ export const CODEX_WS_CREATE_FRAME_LIMIT_BYTES =
49
51
  /** Close code the backend uses for an oversized message (RFC 6455 "message too big"). */
50
52
  const WS_CLOSE_MESSAGE_TOO_BIG = 1009;
51
53
 
52
- const codexWsUpstreamResponses = new WeakSet<Response>();
53
- const quotaObservedResponses = new WeakSet<Response>();
54
- const codexWsPreludeProjections = new WeakSet<Response>();
54
+ const codexWsUpstreamResponses = new WeakMap<Response, true>();
55
+ const quotaObservedResponses = new WeakMap<Response, true>();
56
+ const codexWsPreludeProjections = new WeakMap<Response, true>();
57
+ registerProviderResponseProvenance(codexWsUpstreamResponses);
58
+ registerProviderResponseProvenance(quotaObservedResponses);
59
+ registerProviderResponseProvenance(codexWsPreludeProjections);
55
60
 
56
61
  /** Quota arrived directly at its captured account; do not replay old HTTP prelude headers. */
57
62
  export function isCodexWsQuotaObservedResponse(response: Response): boolean {
@@ -65,7 +70,7 @@ export function isCodexWsUpstreamResponse(response: Response): boolean {
65
70
 
66
71
  /** Pre-response projection; its headers retain the exchange's prelude snapshot. */
67
72
  export function markCodexWsPreludeProjection(response: Response): void {
68
- codexWsPreludeProjections.add(response);
73
+ codexWsPreludeProjections.set(response, true);
69
74
  }
70
75
 
71
76
  export function isCodexWsPreludeProjection(response: Response): boolean {
@@ -73,8 +78,8 @@ export function isCodexWsPreludeProjection(response: Response): boolean {
73
78
  }
74
79
 
75
80
  export function markCodexWsResponse(response: Response, observed: boolean): void {
76
- codexWsUpstreamResponses.add(response);
77
- if (observed) quotaObservedResponses.add(response);
81
+ codexWsUpstreamResponses.set(response, true);
82
+ if (observed) quotaObservedResponses.set(response, true);
78
83
  }
79
84
 
80
85
  /**
@@ -313,6 +318,7 @@ export function restoreCodexWsPreOutputRefusal(response: Response): Response {
313
318
  }
314
319
 
315
320
  const codexWsStageByResponse = new WeakMap<Response, CodexWsStageRecord>();
321
+ registerProviderResponseProvenance(codexWsStageByResponse);
316
322
 
317
323
  export function markCodexWsStage(response: Response, record: CodexWsStageRecord): void {
318
324
  const current = codexWsStageByResponse.get(response);
@@ -457,6 +463,7 @@ export function projectCodexWsFailure(
457
463
  }
458
464
 
459
465
  const socketDeathStages = new WeakMap<Response, RequestFailureStage>();
466
+ registerProviderResponseProvenance(socketDeathStages);
460
467
 
461
468
  /**
462
469
  * Record that a pre-response settle came from the socket closing or failing under the send (#4191),
@@ -517,12 +524,12 @@ export function normalizeResponsesWsRelayEvent(text: string): ResponsesWsRelayEv
517
524
  return null;
518
525
  }
519
526
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) return null;
520
- const record = payload as Record<string, unknown>;
527
+ const record = normalizeResponsesIncompleteReason(payload as Record<string, unknown>);
521
528
  if (typeof record.type !== "string") return null;
522
529
  // A native error may be pretty-printed JSON. SSE prefixes one data line;
523
530
  // embedded physical newlines would otherwise truncate the JSON for readers.
524
531
  if (record.type !== "response.done") return {
525
- type: record.type, text: /[\r\n]/.test(text) ? JSON.stringify(record) : text, payload: record,
532
+ type: record.type, text: record !== payload || /[\r\n]/.test(text) ? JSON.stringify(record) : text, payload: record,
526
533
  };
527
534
 
528
535
  const response = record.response;