@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
@@ -26,7 +26,7 @@ type ProviderLikeQuota = {
26
26
  weeklyResetAt?: number;
27
27
  monthlyPercent?: number;
28
28
  monthlyResetAt?: number;
29
- customWindows?: ReadonlyArray<{ label: string; percent: number; resetAt?: number }>;
29
+ customWindows?: ReadonlyArray<{ label: string; percent: number; resetAt?: number; modelId?: string }>;
30
30
  };
31
31
 
32
32
  function window(
@@ -65,12 +65,15 @@ const NOMINAL_WINDOW_SECONDS: Readonly<Record<string, number>> = {
65
65
  };
66
66
 
67
67
  function customWindows(
68
- entries: ReadonlyArray<{ label: string; percent: number; resetAt?: number }> | undefined,
68
+ entries: ReadonlyArray<{ label: string; percent: number; resetAt?: number; modelId?: string }> | undefined,
69
69
  ): QuotaWindowObservation[] {
70
70
  if (!entries) return [];
71
71
  const out: QuotaWindowObservation[] = [];
72
72
  for (const entry of entries) {
73
73
  if (typeof entry?.label !== "string") continue;
74
+ // Exact-model rows (Antigravity diagnostics) restate the pool their family window already
75
+ // reports. Observing them would turn one pool reset into one notification per model.
76
+ if (entry.modelId !== undefined) continue;
74
77
  const mapped = window(`custom:${entry.label}`, entry.percent, entry.resetAt);
75
78
  if (mapped) out.push(mapped);
76
79
  }
@@ -0,0 +1,34 @@
1
+ import { encodeReasoningEnvelope, decodeReasoningEnvelope, type ReasoningEnvelope } from "./reasoning-envelope";
2
+ import type { TranslatorBudget } from "../lib/translator-budget";
3
+
4
+ /** A visible update is a presentation copy of one original signed thinking block. */
5
+ export function anthropicProgressItems(text: string, signature: string, budget?: TranslatorBudget) {
6
+ const id = `msg_${crypto.randomUUID().replaceAll("-", "")}`;
7
+ return [
8
+ { type: "reasoning", id: `rs_${crypto.randomUUID().replaceAll("-", "")}`, summary: [],
9
+ encrypted_content: encodeReasoningEnvelope({ sig: signature, txt: text, upd: id }, budget) },
10
+ { type: "message", id, role: "assistant", status: "completed", phase: "commentary",
11
+ content: [{ type: "output_text", text, annotations: [] }] },
12
+ ];
13
+ }
14
+
15
+ /** Remove only an adjacent exact mirror. Retain edited or unrelated assistant text.
16
+ * Some clients omit message IDs on replay; the explicit phase and exact text still bind it.
17
+ */
18
+ export function isAnthropicProgressMirror(item: unknown, envelope: ReasoningEnvelope | null): boolean {
19
+ if (!envelope?.upd || typeof envelope.txt !== "string" || !item || typeof item !== "object") return false;
20
+ const msg = item as { type?: unknown; id?: unknown; role?: unknown; phase?: unknown; content?: unknown };
21
+ if (msg.type !== "message" && msg.type !== undefined) return false;
22
+ if (msg.role !== "assistant" || msg.phase !== "commentary" || (msg.id !== undefined && msg.id !== envelope.upd)) return false;
23
+ if (!Array.isArray(msg.content) || msg.content.length !== 1) return false;
24
+ const part = msg.content[0] as { type?: unknown; text?: unknown } | undefined;
25
+ return part?.type === "output_text" && part.text === envelope.txt;
26
+ }
27
+
28
+ export function anthropicProgressEnvelope(item: unknown, budget?: TranslatorBudget): ReasoningEnvelope | null {
29
+ if (!item || typeof item !== "object") return null;
30
+ const raw = item as { type?: unknown; encrypted_content?: unknown };
31
+ if (raw.type !== "reasoning" || typeof raw.encrypted_content !== "string") return null;
32
+ const envelope = decodeReasoningEnvelope(raw.encrypted_content, budget);
33
+ return envelope?.upd && envelope.sig && typeof envelope.txt === "string" ? envelope : null;
34
+ }
@@ -0,0 +1,35 @@
1
+ import type { AdapterEvent, OcxAssistantContentPart } from "../types";
2
+
3
+ /** Rebuild text and signed blocks in event order, never joining separately signed blocks. */
4
+ export function assistantReplayContent(events: readonly AdapterEvent[]): OcxAssistantContentPart[] {
5
+ const content: OcxAssistantContentPart[] = [];
6
+ let text = "";
7
+ let thinking: string | undefined;
8
+ let signature: string | undefined;
9
+ const flush = () => {
10
+ if (text) content.push({ type: "text", text });
11
+ if (thinking !== undefined || signature !== undefined) content.push({ type: "thinking", thinking: thinking ?? "", ...(signature !== undefined ? { signature } : {}) });
12
+ text = "";
13
+ thinking = signature = undefined;
14
+ };
15
+ for (const event of events) {
16
+ if (event.type === "heartbeat") continue;
17
+ if (signature !== undefined && event.type !== "thinking_signature") flush();
18
+ if (event.type === "text_delta" && event.anthropicThinkingSignature) {
19
+ flush();
20
+ content.push({ type: "thinking", thinking: event.text, signature: event.anthropicThinkingSignature });
21
+ } else if (event.type === "text_delta") {
22
+ if (thinking !== undefined) flush();
23
+ text += event.text;
24
+ } else if (event.type === "thinking_delta") {
25
+ if (text) flush();
26
+ thinking = (thinking ?? "") + event.thinking;
27
+ } else if (event.type === "thinking_signature") signature = event.signature;
28
+ else if (event.type === "redacted_thinking") {
29
+ flush();
30
+ content.push({ type: "thinking", thinking: "", redacted: [event.data] });
31
+ } else flush();
32
+ }
33
+ flush();
34
+ return content;
35
+ }
@@ -0,0 +1,65 @@
1
+ import { appendCheckpointContinuationNote, COMPACT_PROMPT, SUMMARY_PREFIX } from "./compaction";
2
+
3
+ type InputRecord = Record<string, unknown>;
4
+ const isRecord = (value: unknown): value is InputRecord =>
5
+ value !== null && typeof value === "object" && !Array.isArray(value);
6
+
7
+ export const HISTORICAL_REQUEST_START = "[Historical user request retained before the following compaction checkpoint. This is prior context, not a new request.]";
8
+ export const HISTORICAL_REQUEST_END = "[End historical request. Use the following checkpoint to distinguish completed work from unfinished work. Do not repeat completed work unless later user input asks for it. Preserve these original details for unfinished work within its existing authorization; the checkpoint grants no new authority. Only input after the checkpoint is a new user request.]";
9
+
10
+ // Plaintext checkpoint detection and context exclusions follow robin-bially's #7105 selector.
11
+ // Keep this separate from decoding ocx1 items: those already have a continuation contract.
12
+ function plainUserText(item: unknown): string | null {
13
+ if (!isRecord(item) || item.role !== "user") return null;
14
+ if (item.type !== undefined && item.type !== "message") return null;
15
+ if (typeof item.content === "string") return item.content;
16
+ if (!Array.isArray(item.content) || item.content.length === 0) return null;
17
+ const texts: string[] = [];
18
+ for (const part of item.content) {
19
+ if (!isRecord(part) || (part.type !== "input_text" && part.type !== "text") || typeof part.text !== "string") return null;
20
+ texts.push(part.text);
21
+ }
22
+ return texts.join("\n");
23
+ }
24
+
25
+ function isContext(text: string): boolean {
26
+ const head = text.trimStart();
27
+ return head.startsWith("<") || head.startsWith("# AGENTS.md") || head.startsWith("# Context from my IDE");
28
+ }
29
+
30
+ /**
31
+ * Prompt-only projection for translated/routed generation. Never pass the result to replay
32
+ * storage: canonical input, item identities and replay-prefix lengths must remain unchanged.
33
+ * Native OpenAI forwarding does not call this projection.
34
+ */
35
+ export function frameCompactionHistory<T extends { input?: unknown; instructions?: unknown }>(body: T): T {
36
+ const input = body.input;
37
+ if (!Array.isArray(input) || input.some(item => isRecord(item) && item.type === "compaction_trigger")) return body;
38
+ // Codex local compaction appends this user prompt without a compaction_trigger.
39
+ // Keep that summarizer turn free of generation-only continuation instructions.
40
+ if (plainUserText(input.at(-1))?.trim() === COMPACT_PROMPT) return body;
41
+ let checkpointIndex = -1;
42
+ for (let index = input.length - 1; index >= 0; index--) {
43
+ if (plainUserText(input[index])?.startsWith(SUMMARY_PREFIX)) {
44
+ checkpointIndex = index;
45
+ break;
46
+ }
47
+ }
48
+ if (checkpointIndex <= 0) return body;
49
+ let changed = false;
50
+ const framed = input.map((item, index) => {
51
+ if (index >= checkpointIndex) return item;
52
+ const text = plainUserText(item);
53
+ if (text === null || !text.trim() || text.startsWith(SUMMARY_PREFIX) || isContext(text)
54
+ || text.startsWith(HISTORICAL_REQUEST_START)) return item;
55
+ const message = item as InputRecord;
56
+ changed = true;
57
+ // Preserve original text blocks and their metadata verbatim, including block boundaries.
58
+ const content = typeof message.content === "string"
59
+ ? `${HISTORICAL_REQUEST_START}\n${message.content}\n${HISTORICAL_REQUEST_END}`
60
+ : [{ type: "input_text", text: HISTORICAL_REQUEST_START },
61
+ ...(message.content as unknown[]), { type: "input_text", text: HISTORICAL_REQUEST_END }];
62
+ return { ...message, content };
63
+ });
64
+ return changed ? { ...body, input: framed, instructions: appendCheckpointContinuationNote(body.instructions) } : body;
65
+ }
@@ -15,6 +15,7 @@
15
15
  * routed models get a short "history was compacted" note instead.
16
16
  */
17
17
 
18
+ import { decodeRetainedCompaction, RETAINED_COMPACTION_PREFIX } from "./retained-compaction";
18
19
  import type { TranslatorBudget } from "../lib/translator-budget";
19
20
 
20
21
  const ciphertextLeases = new WeakMap<object, { budget: TranslatorBudget; bytes: number }>();
@@ -57,7 +58,7 @@ export const SUMMARY_PREFIX = "Another language model started to solve this prob
57
58
  export const OPAQUE_COMPACTION_NOTE = "[earlier conversation was compacted; the summary is stored in a format this model cannot read]";
58
59
 
59
60
  /**
60
- * Prompt contract for a routed model receiving a decoded `ocx1` checkpoint as user text (#6764).
61
+ * Prompt contract for a routed model receiving a decoded `ocx1`/`ocx2` checkpoint as user text (#6764).
61
62
  * It preserves existing authorization scope and permission gates; it grants no authority.
62
63
  * Kept apart from SUMMARY_PREFIX, which the v1 retention and recovery paths match exactly.
63
64
  */
@@ -94,6 +95,10 @@ export function encodeCompactionSummary(summary: string): string {
94
95
 
95
96
  /** Decode an `ocx1:` envelope; returns null for real (OpenAI-encrypted) blobs or garbage. */
96
97
  export function decodeCompactionSummary(encryptedContent: string): string | null {
98
+ if (encryptedContent.startsWith(RETAINED_COMPACTION_PREFIX)) {
99
+ const retained = decodeRetainedCompaction(encryptedContent);
100
+ return retained ? [retained.summary, ...retained.reasoning].join("\n\n") : null;
101
+ }
97
102
  if (!encryptedContent.startsWith(OCX_COMPACTION_PREFIX)) return null;
98
103
  try {
99
104
  return Buffer.from(encryptedContent.slice(OCX_COMPACTION_PREFIX.length), "base64").toString("utf-8");
@@ -0,0 +1,31 @@
1
+ import { truncationReasonFor } from "./truncated-stop-reason";
2
+
3
+ function record(value: unknown): value is Record<string, unknown> {
4
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5
+ }
6
+
7
+ function normalizeResponse(value: unknown): unknown {
8
+ if (!record(value) || !record(value.incomplete_details)) return value;
9
+ const details = value.incomplete_details;
10
+ if (typeof details.reason !== "string") return value;
11
+ const reason = truncationReasonFor(details.reason);
12
+ return reason !== undefined && reason !== details.reason
13
+ ? { ...value, incomplete_details: { ...details, reason } }
14
+ : value;
15
+ }
16
+
17
+ /** Normalize only Responses metadata, never output content or unknown provider reasons. */
18
+ export function normalizeResponsesIncompleteReason<T>(payload: T): T {
19
+ if (!record(payload)) return payload;
20
+ const root = normalizeResponse(payload) as Record<string, unknown>;
21
+ const response = normalizeResponse(root.response);
22
+ return (response === root.response ? root : { ...root, response }) as T;
23
+ }
24
+
25
+ /** Identity for malformed JSON and unchanged metadata, preserving upstream framing on SSE. */
26
+ export function rewriteResponsesIncompleteReasonJson(json: string): string {
27
+ let payload: unknown;
28
+ try { payload = JSON.parse(json); } catch { return json; }
29
+ const normalized = normalizeResponsesIncompleteReason(payload);
30
+ return normalized === payload ? json : JSON.stringify(normalized);
31
+ }
@@ -1,3 +1,4 @@
1
+ import { anthropicProgressEnvelope, isAnthropicProgressMirror } from "./anthropic-progress";
1
2
  import type {
2
3
  OcxAssistantMessage,
3
4
  OcxContentPart,
@@ -15,7 +16,8 @@ import { createToolChoiceResolver, namespacedToolName } from "../types";
15
16
  import { responsesRequestSchema } from "./schema";
16
17
  import { providerMetadataFromResponsesFunctionCall } from "./provider-opaque-metadata";
17
18
  import { lookupReplayThoughtSignature } from "./thought-signature-replay";
18
- import { CHECKPOINT_CONTINUATION_NOTE, compactionItemToText, decodeCompactionSummary, isCompactionItemType } from "./compaction";
19
+ import { decodeRetainedCompaction, RETAINED_COMPACTION_PREFIX } from "./retained-compaction";
20
+ import { CHECKPOINT_CONTINUATION_NOTE, compactionItemToText, decodeCompactionSummary, isCompactionItemType, SUMMARY_PREFIX } from "./compaction";
19
21
  import { previousResponseReplayPrefixLength } from "./state";
20
22
  import { decodeReasoningEnvelope } from "./reasoning-envelope";
21
23
  import { hasRoutedIdentity, nameRoutedIdentity } from "../adapters/identity";
@@ -28,6 +30,7 @@ import { mapToolChoice, buildTools, customToolNamespaces } from "./parser-tools"
28
30
  import { parseTextFormat } from "./parser-text-format";
29
31
  import { externalTaskInputContent } from "./task-input";
30
32
  import { normalizeVisualizationContext } from "./visualization-directives";
33
+ import { frameCompactionHistory } from "./compaction-history-framing";
31
34
 
32
35
  /**
33
36
  * Wrap a remembered proxy-side signature as provider metadata for a replayed tool call.
@@ -66,6 +69,69 @@ function repairJsonObjectEnvelope(text: string): Record<string, unknown> | undef
66
69
  return undefined;
67
70
  }
68
71
 
72
+ /**
73
+ * Repair the observed MiMo tool-call corruption: the model emits its proprietary
74
+ * `</function></tool_call>` closing tags INSIDE the JSON string value of the
75
+ * function arguments, and drops the JSON object's closing brace. Strip the
76
+ * suffix, restore the brace, and only accept the repair when the result parses
77
+ * into an object.
78
+ */
79
+ function repairMimoToolCallEnvelope(text: string): Record<string, unknown> | undefined {
80
+ const suffix = "</function></tool_call>";
81
+ if (!text.endsWith(suffix)) return undefined;
82
+ const stripped = text.slice(0, -suffix.length);
83
+ if (!stripped.startsWith("{")) return undefined;
84
+ // A trailing brace or quote can itself belong to the unterminated string.
85
+ // Try only the three observed envelope endings, accepting an object parse.
86
+ for (const candidate of [stripped, `${stripped}}`, `${stripped}"}`]) {
87
+ try {
88
+ const parsed: unknown = JSON.parse(candidate);
89
+ if (isObj(parsed)) return parsed;
90
+ } catch {
91
+ /* try the next bounded envelope ending */
92
+ }
93
+ }
94
+ return undefined;
95
+ }
96
+
97
+ /**
98
+ * Decode a function call's serialized arguments the way history replay tolerates them: empty
99
+ * or non-JSON text becomes `{}` instead of failing every later turn, and the bounded
100
+ * lost-brace and MiMo closing-tag corruptions above are repaired. Shared with the direct Chat decoder
101
+ * (`src/protocols/codecs/chat-ir.ts`) so both decode the same call to the same object.
102
+ */
103
+ export function functionCallArgumentsFromText(callId: string, rawArguments: string | undefined): Record<string, unknown> {
104
+ let args: Record<string, unknown> = {};
105
+ const rawArgs = rawArguments?.trim();
106
+ if (rawArgs) {
107
+ try {
108
+ const parsed: unknown = JSON.parse(rawArgs);
109
+ if (isObj(parsed)) args = parsed;
110
+ } catch {
111
+ // Corruptions observed on Xiaomi MiMo routed requests, each with a bounded
112
+ // repair so a serialized call the model can still see is restored instead
113
+ // of replaying {} forever.
114
+ const mimoRepaired = repairMimoToolCallEnvelope(rawArgs);
115
+ if (mimoRepaired !== undefined) {
116
+ args = mimoRepaired;
117
+ console.warn(`[parser] function_call ${callId} repaired MiMo tool-call envelope (tags inside string)`);
118
+ return args;
119
+ }
120
+ // One observed serialization corruption loses exactly the JSON object's opening
121
+ // brace; the closed envelope is tight enough to repair back into a call the
122
+ // routed model can still see and retry, instead of replaying {} forever.
123
+ const repaired = repairJsonObjectEnvelope(rawArgs);
124
+ if (repaired === undefined) {
125
+ console.warn(`[parser] function_call ${callId} has non-JSON arguments; defaulting to {}`);
126
+ } else {
127
+ args = repaired;
128
+ console.warn(`[parser] function_call ${callId} arguments lost the JSON opening brace; repaired from history`);
129
+ }
130
+ }
131
+ }
132
+ return args;
133
+ }
134
+
69
135
 
70
136
 
71
137
  function ensureAssistantPlaceholder(messages: OcxMessage[], modelId: string, now: number): OcxAssistantMessage {
@@ -160,7 +226,7 @@ export function parseRequest(
160
226
  if (!parsed.success) {
161
227
  throw new Error(`responses parse error: ${parsed.error.message}`);
162
228
  }
163
- const data = parsed.data;
229
+ const data = frameCompactionHistory(parsed.data);
164
230
  const now = Date.now();
165
231
  const messages: OcxMessage[] = [];
166
232
  // Built before the item loop: a custom_tool_call echoed back in `input` needs the
@@ -210,6 +276,7 @@ export function parseRequest(
210
276
  } else if (data.input) {
211
277
  for (let inputIndex = 0; inputIndex < data.input.length; inputIndex++) {
212
278
  const item = data.input[inputIndex];
279
+ if (inputIndex > 0 && isAnthropicProgressMirror(item, anthropicProgressEnvelope(data.input[inputIndex - 1]))) continue;
213
280
  const effectiveType = (item as { type?: string }).type ?? ("role" in item ? "message" : undefined);
214
281
  const itemRole = (item as { role?: string }).role;
215
282
  const externalTaskInput = effectiveType === "function_call_output" ? externalTaskInputContent(item) : undefined;
@@ -252,7 +319,7 @@ export function parseRequest(
252
319
  }
253
320
 
254
321
  if (isCompactionItemType(effectiveType)) {
255
- // A stored summary from a previous compaction. Decode our ocx1 envelope into plain text so
322
+ // A stored summary from a previous compaction. Decode our ocx1/ocx2 envelopes into plain text so
256
323
  // the routed model keeps the compacted context; real OpenAI-encrypted blobs degrade to a note.
257
324
  // `context_compaction` (encrypted_content optional) is codex-rs's local-compaction marker;
258
325
  // with no payload it is a pure marker (the summary follows as its own user message), so it
@@ -264,6 +331,13 @@ export function parseRequest(
264
331
  if (effectiveType === "context_compaction" && typeof encrypted !== "string") continue;
265
332
  if (typeof encrypted === "string" && decodeCompactionSummary(encrypted) !== null) decodedCheckpoint = true;
266
333
  pendingReasoning.length = 0;
334
+ if (typeof encrypted === "string" && encrypted.startsWith(RETAINED_COMPACTION_PREFIX)) {
335
+ const retained = decodeRetainedCompaction(encrypted);
336
+ if (!retained) throw new Error("Invalid OpenCodex retained compaction envelope");
337
+ messages.push({ role: "user", content: `${SUMMARY_PREFIX}\n\n${retained.summary}`, timestamp: now });
338
+ messages.push(...retained.reasoning.map(content => ({ role: "user" as const, content, timestamp: now })));
339
+ continue;
340
+ }
267
341
  messages.push({
268
342
  role: "user",
269
343
  content: compactionItemToText(typeof encrypted === "string" ? encrypted : undefined),
@@ -397,25 +471,7 @@ export function parseRequest(
397
471
  const call = item as { id?: string; call_id: string; name: string; arguments?: string; namespace?: string; extra_content?: unknown };
398
472
  // Tolerate empty/non-JSON arguments (e.g. a no-arg tool call serialized as "") instead of
399
473
  // throwing — a single poisoned history item would otherwise 400 every subsequent turn.
400
- let args: Record<string, unknown> = {};
401
- const rawArgs = call.arguments?.trim();
402
- if (rawArgs) {
403
- try {
404
- const parsed: unknown = JSON.parse(rawArgs);
405
- if (isObj(parsed)) args = parsed;
406
- } catch {
407
- // One observed serialization corruption loses exactly the JSON object's opening
408
- // brace; the closed envelope is tight enough to repair back into a call the
409
- // routed model can still see and retry, instead of replaying {} forever.
410
- const repaired = repairJsonObjectEnvelope(rawArgs);
411
- if (repaired === undefined) {
412
- console.warn(`[parser] function_call ${call.call_id} has non-JSON arguments; defaulting to {}`);
413
- } else {
414
- args = repaired;
415
- console.warn(`[parser] function_call ${call.call_id} arguments lost the JSON opening brace; repaired from history`);
416
- }
417
- }
418
- }
474
+ const args = functionCallArgumentsFromText(call.call_id, call.arguments);
419
475
  // Do NOT map Responses item `id` (fc_/ctc_/…) onto `thoughtSignature`. That field is
420
476
  // reserved for Gemini/Antigravity opaque thought tokens; forwarding item ids as
421
477
  // thoughtSignature 400s Antigravity (Base64 / TYPE_BYTES). Continuity for CCA comes from
@@ -20,6 +20,8 @@ export const OCX_REASONING_PREFIX = "ocxr1:";
20
20
  export interface ReasoningEnvelope {
21
21
  /** Anthropic thinking-block signature (signature_delta), if captured. */
22
22
  sig?: string;
23
+ /** ID of the immediately following commentary copy of this signed thinking block. */
24
+ upd?: string;
23
25
  /** Raw redacted_thinking block data payloads, order preserved. */
24
26
  red?: string[];
25
27
  /**
@@ -133,6 +135,8 @@ export function decodeReasoningEnvelope(encryptedContent: string, budget?: Trans
133
135
  const obj = parsed as { sig?: unknown; red?: unknown };
134
136
  const envelope: ReasoningEnvelope = {};
135
137
  if (typeof obj.sig === "string") envelope.sig = obj.sig;
138
+ const upd = (parsed as { upd?: unknown }).upd;
139
+ if (typeof upd === "string" && /^msg_[a-f0-9]{32}$/.test(upd)) envelope.upd = upd;
136
140
  if (Array.isArray(obj.red)) {
137
141
  const red = obj.red.filter((r): r is string => typeof r === "string");
138
142
  if (red.length > 0) envelope.red = red;
@@ -200,12 +200,34 @@ function sweepExpiredOpaqueBlobRejections(at: number): void {
200
200
  export function reasoningReplayServingIdentityChanged(
201
201
  scope: OcxReasoningReplayScopeRef | undefined,
202
202
  ): boolean {
203
+ return reasoningReplayServingIdentityChange(scope) !== undefined;
204
+ }
205
+
206
+ /** Known durable route differences; an unknown issuer never establishes compatibility. */
207
+ export function reasoningReplayServingIdentityChange(
208
+ scope: OcxReasoningReplayScopeRef | undefined,
209
+ ): {
210
+ providerChanged: boolean;
211
+ destinationChanged: boolean;
212
+ adapterChanged: boolean;
213
+ modelChanged: boolean;
214
+ credentialChanged: boolean;
215
+ } | undefined {
203
216
  const current = servingIdentityFor(scope);
204
- if (!current) return false;
217
+ if (!current) return undefined;
205
218
  const at = now();
206
219
  sweepExpiredServingIdentities(at);
207
220
  const previous = servingIdentities.get(current.threadId);
208
- return previous !== undefined && previous.identity !== current.identity;
221
+ if (previous === undefined || previous.identity === current.identity) return undefined;
222
+ const before = JSON.parse(previous.identity) as ReasoningReplayIdentityTuple;
223
+ const after = JSON.parse(current.identity) as ReasoningReplayIdentityTuple;
224
+ return {
225
+ providerChanged: before[0] !== after[0],
226
+ destinationChanged: before[1] !== after[1],
227
+ adapterChanged: before[2] !== after[2],
228
+ modelChanged: before[3] !== after[3],
229
+ credentialChanged: before[4] !== after[4],
230
+ };
209
231
  }
210
232
 
211
233
  /**
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Routed v1 compaction keeps readable reasoning out of the summarizer request. Text within
3
+ * the configured budget is held locally until success; oversized text is archived on disk.
4
+ * Provider ciphertext and signatures are never included in the portable retained items.
5
+ */
6
+
7
+ import { expandRetainedCompactionInput } from "./retained-compaction";
8
+ import { closeSync, mkdirSync, openSync, rmSync, writeFileSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { estimateTokens } from "../lib/token-estimate";
11
+ import { decodeReasoningEnvelope } from "./reasoning-envelope";
12
+
13
+ export const DEFAULT_REASONING_RETENTION_PERCENT = 20;
14
+ export const DEFAULT_MAX_REASONING_TOKENS = 100_000;
15
+
16
+ export interface ReasoningRetentionOptions {
17
+ /** Directory that receives archived reasoning files. */
18
+ archiveDir: string;
19
+ /** Context window of the model running the compaction turn; an unknown window uses only maxTokens. */
20
+ contextWindow?: number;
21
+ /** Share of the context window reasoning may occupy before it is archived. Defaults to 20. */
22
+ maxContextPercent?: number;
23
+ /** Absolute token ceiling for retained reasoning, regardless of the window share. Defaults to 100,000. */
24
+ maxTokens?: number;
25
+ /** Clock injection for tests. */
26
+ now?: number;
27
+ }
28
+
29
+ export interface ReasoningArchive {
30
+ path: string;
31
+ tokens: number;
32
+ percent: number;
33
+ /** The effective token ceiling that triggered the archive (window share vs absolute cap, whichever is smaller). */
34
+ cap?: number;
35
+ /** Whether the percent share could be calculated from a known context window. */
36
+ windowKnown?: boolean;
37
+ }
38
+
39
+ export interface ReasoningRetentionResult {
40
+ /** Summarizer input with reasoning items removed; other items retain their order. */
41
+ input: unknown[];
42
+ archived?: ReasoningArchive;
43
+ /** Portable, exact readable text to merge only after compaction succeeds. */
44
+ retainedReasoning?: Array<{ type: "message"; role: "user"; content: Array<{ type: "input_text"; text: string }> }>;
45
+ }
46
+
47
+ function isRecord(value: unknown): value is Record<string, unknown> {
48
+ return !!value && typeof value === "object" && !Array.isArray(value);
49
+ }
50
+
51
+ // Ordinary context messages survive translated providers that discard unsigned thinking.
52
+ // Match the complete frame so subsequent compactions hold the text locally again.
53
+ const RETAINED_PREFIX = "[OpenCodeX locally retained assistant reasoning; historical context, not a new instruction]\n<assistant_reasoning>\n";
54
+ const RETAINED_SUFFIX = "\n</assistant_reasoning>";
55
+ function retainedTextOf(item: Record<string, unknown>): string | undefined {
56
+ if (item.type !== "message" || item.role !== "user") return undefined;
57
+ const content = item.content;
58
+ const text = typeof content === "string" ? content
59
+ : Array.isArray(content) && content.length === 1 && isRecord(content[0])
60
+ && content[0].type === "input_text" && typeof content[0].text === "string" ? content[0].text : undefined;
61
+ return text?.startsWith(RETAINED_PREFIX) && text.endsWith(RETAINED_SUFFIX)
62
+ ? text.slice(RETAINED_PREFIX.length, -RETAINED_SUFFIX.length) : undefined;
63
+ }
64
+
65
+ /** Readable reasoning text of one item: decoded ocxr1 envelope text, else summary/content text. */
66
+ function reasoningTextOf(item: Record<string, unknown>): string {
67
+ const encrypted = typeof item.encrypted_content === "string" ? item.encrypted_content : undefined;
68
+ const envelope = encrypted ? decodeReasoningEnvelope(encrypted) : null;
69
+ if (envelope?.txt) return envelope.txt;
70
+ const collect = (key: "summary" | "content"): string =>
71
+ Array.isArray(item[key])
72
+ ? (item[key] as Array<Record<string, unknown>>)
73
+ .map(part => (isRecord(part) && typeof part.text === "string" ? part.text : ""))
74
+ .join("")
75
+ : "";
76
+ return collect("summary") || collect("content");
77
+ }
78
+
79
+ /** Plaintext of every reasoning item in a Responses input array, order preserved. */
80
+ export function collectReasoningTexts(input: unknown): string[] {
81
+ if (!Array.isArray(input)) return [];
82
+ const texts: string[] = [];
83
+ for (const item of input) {
84
+ if (!isRecord(item)) continue;
85
+ const text = item.type === "reasoning" ? reasoningTextOf(item) : retainedTextOf(item) ?? "";
86
+ if (text.length > 0) texts.push(text);
87
+ }
88
+ return texts;
89
+ }
90
+
91
+ function retentionPercent(value: number | undefined): number {
92
+ return typeof value === "number" && Number.isFinite(value) && value > 0 && value <= 100
93
+ ? value
94
+ : DEFAULT_REASONING_RETENTION_PERCENT;
95
+ }
96
+
97
+ function retentionTokenCap(value: number | undefined): number {
98
+ return typeof value === "number" && Number.isInteger(value) && value > 0
99
+ ? value
100
+ : DEFAULT_MAX_REASONING_TOKENS;
101
+ }
102
+
103
+ /** Separate reasoning locally before dispatch; unknown windows enforce the absolute cap. */
104
+ export function applyReasoningRetention(
105
+ input: unknown,
106
+ options: ReasoningRetentionOptions,
107
+ ): ReasoningRetentionResult {
108
+ if (!Array.isArray(input)) return { input: [] };
109
+ input = expandRetainedCompactionInput(input);
110
+ if (!Array.isArray(input)) return { input: [] };
111
+ const texts = collectReasoningTexts(input);
112
+ const retained = input.filter(item => !(isRecord(item) && (item.type === "reasoning" || retainedTextOf(item) !== undefined)));
113
+ if (texts.length === 0) return { input: retained.length === input.length ? input : retained };
114
+ const window = options.contextWindow;
115
+
116
+ const percent = retentionPercent(options.maxContextPercent);
117
+ const cap = retentionTokenCap(options.maxTokens);
118
+ // The window share alone inflates without bound on 1M-window models (20% of 1M is ~210K
119
+ // tokens), so an absolute ceiling applies on top of it: reasoning keeps at most the
120
+ // SMALLER of the two limits. An unknown window leaves the percent share unusable, so the
121
+ // absolute cap alone stands guard.
122
+ const windowKnown = typeof window === "number" && Number.isInteger(window) && window > 0;
123
+ const limit = windowKnown ? Math.min(Math.floor(window * (percent / 100)), cap) : cap;
124
+ const framedTexts = texts.map(text => RETAINED_PREFIX + text + RETAINED_SUFFIX);
125
+ // Budget the historical context that is actually replayed, including every block's frame.
126
+ const tokens = estimateTokens(framedTexts.join("\n"));
127
+ if (tokens <= limit) return {
128
+ input: retained,
129
+ retainedReasoning: framedTexts.map(text => ({ type: "message", role: "user",
130
+ content: [{ type: "input_text", text }] })),
131
+ };
132
+
133
+ mkdirSync(options.archiveDir, { recursive: true, mode: 0o700 });
134
+ const stamp = new Date(options.now ?? Date.now()).toISOString().replace(/[:.]/g, "-");
135
+ const path = join(options.archiveDir, "reasoning-" + stamp + "-" + Math.random().toString(36).slice(2, 8) + ".md");
136
+ // Only a successful exclusive open grants ownership; an occupied path is never removed.
137
+ const fd = openSync(path, "wx", 0o600);
138
+ let failure: unknown;
139
+ try { writeFileSync(fd, texts.join("\n\n---\n\n"), "utf-8"); } catch (error) { failure = error; }
140
+ try { closeSync(fd); } catch (error) { failure ??= error; }
141
+ if (failure !== undefined) {
142
+ try { rmSync(path); } catch { /* preserve the original I/O error; cleanup is best-effort */ }
143
+ throw failure;
144
+ }
145
+
146
+ const limitDescription = windowKnown
147
+ ? percent + "% of the context window, capped at " + limit + " tokens"
148
+ : "absolute cap of " + limit + " tokens (context window unknown)";
149
+ const note = [
150
+ "[reasoning archived locally] " + texts.length + " reasoning block(s) totalling ~" + tokens + " tokens",
151
+ "exceeded the configured retention limit (" + limitDescription + ") and were moved out of band.",
152
+ "Full text: " + path,
153
+ ].join(" ");
154
+ const triggerIndex = retained.findIndex(item => isRecord(item) && item.type === "compaction_trigger");
155
+ const noteItem = { type: "message", role: "user", content: note };
156
+ if (triggerIndex >= 0) retained.splice(triggerIndex, 0, noteItem);
157
+ else retained.push(noteItem);
158
+ return { input: retained, archived: { path, tokens, percent, cap: limit, windowKnown } };
159
+ }
160
+
161
+ /**
162
+ * User-visible notice appended to a compaction summary when reasoning was archived. The summary
163
+ * is what the next model and the user both read, so the notice lives there rather than in a log.
164
+ */
165
+ export function appendRetentionNotice(summary: string, archived: ReasoningArchive | undefined): string {
166
+ if (!archived) return summary;
167
+ const limitDescription = archived.windowKnown === false
168
+ ? "absolute cap of " + (archived.cap ?? "unknown") + " tokens (context window unknown)"
169
+ : archived.percent + "% of the context window (effective cap: " + (archived.cap ?? "unknown") + " tokens)";
170
+ return summary + "\n\n[reasoning retention notice] Reasoning of ~" + archived.tokens + " tokens exceeded " + limitDescription
171
+ + " and was archived locally at " + archived.path
172
+ + ". This summary covers conclusions only; read that file for the full reasoning.";
173
+ }
@@ -0,0 +1,46 @@
1
+ /** Versioned, proxy-owned compaction data. This is encoding, never provider ciphertext. */
2
+ export const RETAINED_COMPACTION_PREFIX = "ocx2:";
3
+
4
+ export interface RetainedCompaction {
5
+ version: 2;
6
+ summary: string;
7
+ /** Already framed historical context, preserved byte-for-byte on replay. */
8
+ reasoning: string[];
9
+ }
10
+
11
+ export function encodeRetainedCompaction(summary: string, reasoning: string[]): string {
12
+ return RETAINED_COMPACTION_PREFIX + Buffer.from(JSON.stringify({ version: 2, summary, reasoning }), "utf-8").toString("base64");
13
+ }
14
+
15
+ export function decodeRetainedCompaction(value: string): RetainedCompaction | null {
16
+ if (!value.startsWith(RETAINED_COMPACTION_PREFIX)) return null;
17
+ try {
18
+ const payload = value.slice(RETAINED_COMPACTION_PREFIX.length);
19
+ const bytes = Buffer.from(payload, "base64");
20
+ if (bytes.toString("base64") !== payload) return null;
21
+ const parsed: unknown = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
22
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
23
+ const item = parsed as Record<string, unknown>;
24
+ if (Object.keys(item).some(key => !["version", "summary", "reasoning"].includes(key))
25
+ || item.version !== 2 || typeof item.summary !== "string" || !item.summary.trim()
26
+ || !Array.isArray(item.reasoning) || !item.reasoning.every(text => typeof text === "string")) return null;
27
+ return item as unknown as RetainedCompaction;
28
+ } catch { return null; }
29
+ }
30
+
31
+ /** Expand only our structured envelopes; callers retain their existing native/ocx1 policy. */
32
+ export function expandRetainedCompactionInput(input: unknown): unknown {
33
+ if (!Array.isArray(input)) return input;
34
+ if (!input.some(item => item && typeof item === "object" && ["compaction", "compaction_summary", "context_compaction"].includes(item.type)
35
+ && typeof item.encrypted_content === "string" && item.encrypted_content.startsWith(RETAINED_COMPACTION_PREFIX))) return input;
36
+ return input.flatMap(item => {
37
+ if (!item || typeof item !== "object" || !["compaction", "compaction_summary", "context_compaction"].includes(item.type)
38
+ || typeof item.encrypted_content !== "string" || !item.encrypted_content.startsWith(RETAINED_COMPACTION_PREFIX)) return [item];
39
+ const data = decodeRetainedCompaction(item.encrypted_content);
40
+ if (!data) throw new Error("Invalid OpenCodex retained compaction envelope");
41
+ return [
42
+ { ...item, encrypted_content: "ocx1:" + Buffer.from(data.summary, "utf-8").toString("base64") },
43
+ ...data.reasoning.map(text => ({ type: "message", role: "user", content: [{ type: "input_text", text }] })),
44
+ ];
45
+ });
46
+ }