@iislee/opencodex 2.11.0 → 2.31.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 (675) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +114 -19
  3. package/bin/ocx.mjs +164 -36
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-BF38heuV.js +104 -0
  6. package/gui/dist/assets/index-DMiI18Kv.css +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +19 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +376 -52
  44. package/src/adapters/base.ts +54 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +601 -0
  48. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  49. package/src/adapters/cursor/cursor-errors.ts +129 -6
  50. package/src/adapters/cursor/discovery.ts +61 -4
  51. package/src/adapters/cursor/effort-map.ts +27 -3
  52. package/src/adapters/cursor/framing.ts +39 -0
  53. package/src/adapters/cursor/h2-pool.ts +123 -0
  54. package/src/adapters/cursor/http1-bidi.ts +361 -0
  55. package/src/adapters/cursor/images.ts +704 -0
  56. package/src/adapters/cursor/live-models.ts +174 -58
  57. package/src/adapters/cursor/live-transport.ts +609 -170
  58. package/src/adapters/cursor/native-exec-common.ts +23 -2
  59. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  60. package/src/adapters/cursor/native-exec-network.ts +1 -1
  61. package/src/adapters/cursor/native-exec-shell.ts +0 -1
  62. package/src/adapters/cursor/native-exec.ts +101 -14
  63. package/src/adapters/cursor/protobuf-events.ts +829 -11
  64. package/src/adapters/cursor/protobuf-request.ts +383 -65
  65. package/src/adapters/cursor/request-builder.ts +237 -31
  66. package/src/adapters/cursor/tool-definitions.ts +242 -12
  67. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  68. package/src/adapters/cursor/transport.ts +22 -0
  69. package/src/adapters/cursor/types.ts +28 -1
  70. package/src/adapters/cursor.ts +133 -7
  71. package/src/adapters/google-antigravity-replay.ts +558 -34
  72. package/src/adapters/google-antigravity-wire.ts +43 -10
  73. package/src/adapters/google-http.ts +38 -10
  74. package/src/adapters/google-tool-schema.ts +80 -15
  75. package/src/adapters/google-truncation.ts +11 -0
  76. package/src/adapters/google.ts +618 -74
  77. package/src/adapters/identity.ts +39 -6
  78. package/src/adapters/kiro-errors.ts +11 -0
  79. package/src/adapters/kiro-events.ts +19 -1
  80. package/src/adapters/kiro-thinking.ts +18 -2
  81. package/src/adapters/kiro-tools.ts +10 -1
  82. package/src/adapters/kiro.ts +92 -54
  83. package/src/adapters/mimo-free.ts +17 -0
  84. package/src/adapters/openai-chat-url.ts +11 -0
  85. package/src/adapters/openai-chat.ts +1311 -336
  86. package/src/adapters/openai-responses-url.ts +16 -0
  87. package/src/adapters/openai-responses.ts +830 -56
  88. package/src/adapters/registry.ts +175 -0
  89. package/src/adapters/responses-tool-schema.ts +67 -0
  90. package/src/adapters/tool-call-id.ts +119 -0
  91. package/src/adapters/tool-catalog-nudge.ts +104 -21
  92. package/src/adapters/xai-web-search.ts +185 -0
  93. package/src/bridge.ts +428 -61
  94. package/src/chat/inbound.ts +43 -19
  95. package/src/chat/outbound.ts +82 -26
  96. package/src/claude/agents-inject.ts +32 -9
  97. package/src/claude/context-windows.ts +21 -5
  98. package/src/claude/desktop-3p.ts +243 -9
  99. package/src/claude/gateway-cache.ts +41 -4
  100. package/src/claude/inbound.ts +72 -3
  101. package/src/claude/model-info.ts +38 -15
  102. package/src/claude/outbound.ts +70 -16
  103. package/src/cli/account-api.ts +35 -1
  104. package/src/cli/account-auth.ts +33 -6
  105. package/src/cli/account-catalog-refresh.ts +14 -0
  106. package/src/cli/account-extended.ts +389 -2
  107. package/src/cli/account-main.ts +317 -0
  108. package/src/cli/account.ts +30 -6
  109. package/src/cli/agent.ts +45 -1
  110. package/src/cli/claude-agent-startup-sync.ts +73 -0
  111. package/src/cli/claude-desktop.ts +29 -4
  112. package/src/cli/claude.ts +103 -34
  113. package/src/cli/codex-log-guard-doctor.ts +103 -0
  114. package/src/cli/codex-shim-autorestore.ts +2 -0
  115. package/src/cli/codex-shim-readiness.ts +76 -0
  116. package/src/cli/combo.ts +8 -0
  117. package/src/cli/config-command.ts +74 -10
  118. package/src/cli/dispatch.ts +593 -0
  119. package/src/cli/doctor.ts +315 -43
  120. package/src/cli/ensure-desired-integrations.ts +152 -0
  121. package/src/cli/export-command.ts +46 -20
  122. package/src/cli/help.ts +20 -271
  123. package/src/cli/index.ts +378 -519
  124. package/src/cli/init.ts +4 -17
  125. package/src/cli/integrations.ts +120 -2
  126. package/src/cli/lab.ts +607 -0
  127. package/src/cli/launcher-context.ts +77 -0
  128. package/src/cli/minimax.ts +497 -0
  129. package/src/cli/models-runtime.ts +35 -2
  130. package/src/cli/models.ts +100 -14
  131. package/src/cli/observe.ts +92 -3
  132. package/src/cli/opencode.ts +4 -2
  133. package/src/cli/provider-runtime.ts +18 -1
  134. package/src/cli/provider.ts +24 -3
  135. package/src/cli/ready.ts +301 -0
  136. package/src/cli/registry.ts +437 -0
  137. package/src/cli/root.ts +86 -0
  138. package/src/cli/route-policy.ts +92 -0
  139. package/src/cli/runtime-api.ts +6 -3
  140. package/src/cli/star-prompt.ts +71 -15
  141. package/src/cli/status.ts +10 -3
  142. package/src/cli/system-restart-client.ts +146 -0
  143. package/src/cli/tray-proxy.ts +153 -6
  144. package/src/cli/v2.ts +105 -10
  145. package/src/cli.ts +1 -1
  146. package/src/clients/config-export.ts +1358 -21
  147. package/src/codex/account-label.ts +14 -1
  148. package/src/codex/account-lifecycle.ts +130 -13
  149. package/src/codex/account-namespaces.ts +49 -3
  150. package/src/codex/account-priority.ts +83 -0
  151. package/src/codex/account-store.ts +29 -2
  152. package/src/codex/account-usability.ts +25 -2
  153. package/src/codex/admission.ts +256 -0
  154. package/src/codex/affinity-debug.ts +162 -0
  155. package/src/codex/app-server-processes.ts +493 -106
  156. package/src/codex/app-server-restart-service.ts +232 -0
  157. package/src/codex/auth-api.ts +849 -242
  158. package/src/codex/auth-collision.ts +5 -3
  159. package/src/codex/auth-context.ts +345 -32
  160. package/src/codex/autostart-health.ts +8 -1
  161. package/src/codex/catalog/account-models.ts +67 -0
  162. package/src/codex/catalog/aggregation.ts +68 -10
  163. package/src/codex/catalog/bundled.ts +331 -33
  164. package/src/codex/catalog/effort.ts +121 -30
  165. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  166. package/src/codex/catalog/kinds.ts +2 -0
  167. package/src/codex/catalog/metadata.ts +529 -45
  168. package/src/codex/catalog/native-models.ts +72 -0
  169. package/src/codex/catalog/parsing.ts +224 -30
  170. package/src/codex/catalog/provider-fetch.ts +1460 -134
  171. package/src/codex/catalog/sync.ts +1449 -186
  172. package/src/codex/catalog-admission.ts +199 -0
  173. package/src/codex/catalog-refresh-status.ts +105 -0
  174. package/src/codex/catalog-write-serialization.ts +242 -0
  175. package/src/codex/catalog.ts +6 -3
  176. package/src/codex/codex-write-lock.ts +384 -0
  177. package/src/codex/convergence-types.ts +614 -0
  178. package/src/codex/convergence.ts +651 -0
  179. package/src/codex/coordinator-doctor.ts +332 -0
  180. package/src/codex/custom-model-catalog-migration.ts +176 -0
  181. package/src/codex/desired-state.ts +230 -0
  182. package/src/codex/features.ts +636 -39
  183. package/src/codex/generation.ts +202 -0
  184. package/src/codex/history-job.ts +407 -0
  185. package/src/codex/history-lock.ts +242 -0
  186. package/src/codex/history-migration-guardian.ts +26 -20
  187. package/src/codex/history-provider.ts +231 -28
  188. package/src/codex/history-transition.ts +105 -0
  189. package/src/codex/history-worker.ts +220 -0
  190. package/src/codex/inject-coordination.ts +290 -0
  191. package/src/codex/inject.ts +1073 -152
  192. package/src/codex/injected-marker.ts +37 -3
  193. package/src/codex/integration-record.ts +266 -0
  194. package/src/codex/internal/catalog-writer.ts +203 -0
  195. package/src/codex/internal/history-writer.ts +80 -0
  196. package/src/codex/journal.ts +66 -4
  197. package/src/codex/log-guard/inspect.ts +506 -0
  198. package/src/codex/log-guard/lock.ts +150 -0
  199. package/src/codex/log-guard/maintenance.ts +403 -0
  200. package/src/codex/log-guard/path-safety.ts +88 -0
  201. package/src/codex/log-guard/policy.ts +44 -0
  202. package/src/codex/log-guard/processes.ts +205 -0
  203. package/src/codex/log-guard/protection.ts +489 -0
  204. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  205. package/src/codex/main-account-cache.ts +24 -0
  206. package/src/codex/main-account.ts +29 -1
  207. package/src/codex/management-convergence.ts +167 -0
  208. package/src/codex/model-cache.ts +56 -10
  209. package/src/codex/model-entitlements.ts +353 -0
  210. package/src/codex/native-main-admission.ts +47 -0
  211. package/src/codex/native-main-auth-temp.ts +187 -0
  212. package/src/codex/native-main-claim.ts +178 -0
  213. package/src/codex/native-main-lock-file.ts +162 -0
  214. package/src/codex/native-main-owner.ts +329 -0
  215. package/src/codex/native-profile-api.ts +247 -0
  216. package/src/codex/native-profile-manager.ts +1531 -0
  217. package/src/codex/native-profile-processes.ts +121 -0
  218. package/src/codex/native-profile-recovery.ts +99 -0
  219. package/src/codex/native-profile-stage-store.ts +387 -0
  220. package/src/codex/native-profile-startup.ts +492 -0
  221. package/src/codex/native-profile-store.ts +855 -0
  222. package/src/codex/native-profile-types.ts +120 -0
  223. package/src/codex/native-residue.ts +682 -0
  224. package/src/codex/paths.ts +80 -1
  225. package/src/codex/plan-from-token.ts +140 -0
  226. package/src/codex/plan.ts +40 -0
  227. package/src/codex/plugins-doctor.ts +1 -1
  228. package/src/codex/pool-rotation.ts +74 -4
  229. package/src/codex/project-config-warnings.ts +20 -6
  230. package/src/codex/prompt-journal.ts +352 -0
  231. package/src/codex/prompt-layers.ts +967 -0
  232. package/src/codex/prompt-lock.ts +143 -0
  233. package/src/codex/quota-rejection.ts +298 -0
  234. package/src/codex/quota.ts +175 -13
  235. package/src/codex/refresh.ts +11 -2
  236. package/src/codex/reset-credit-recovery.ts +1044 -0
  237. package/src/codex/routing.ts +505 -94
  238. package/src/codex/runtime.ts +159 -38
  239. package/src/codex/shim.ts +1009 -28
  240. package/src/codex/subagent-model-fallback.ts +350 -35
  241. package/src/codex/sync.ts +191 -2
  242. package/src/codex/transition-state.ts +612 -0
  243. package/src/codex/upstream-host-health.ts +368 -0
  244. package/src/codex/user-identity.ts +557 -0
  245. package/src/codex/warmup.ts +187 -81
  246. package/src/codex/write-coordination.ts +114 -0
  247. package/src/combos/failover.ts +20 -0
  248. package/src/combos/index.ts +4 -0
  249. package/src/combos/request.ts +32 -0
  250. package/src/combos/types.ts +81 -9
  251. package/src/config/provider-name.ts +24 -0
  252. package/src/config.ts +1762 -140
  253. package/src/generated/compatibility-version.json +3164 -0
  254. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  255. package/src/grok/inject.ts +16 -5
  256. package/src/grok/inspect.ts +45 -0
  257. package/src/grok/sync.ts +2 -2
  258. package/src/images/loop.ts +152 -29
  259. package/src/images/plan.ts +23 -13
  260. package/src/integrations/config-io.ts +269 -0
  261. package/src/integrations/journal.ts +315 -0
  262. package/src/integrations/merge.ts +135 -0
  263. package/src/integrations/mutation-flight.ts +71 -0
  264. package/src/integrations/native/ownership-preflight.ts +202 -0
  265. package/src/integrations/omp-yaml-source.ts +358 -0
  266. package/src/integrations/owned-refresh.ts +74 -0
  267. package/src/integrations/ownership.ts +111 -0
  268. package/src/integrations/registry.ts +159 -0
  269. package/src/integrations/serialize.ts +314 -0
  270. package/src/integrations/state.ts +361 -0
  271. package/src/integrations/store.ts +103 -0
  272. package/src/integrations/writer-lock.ts +98 -0
  273. package/src/integrations/writer.ts +691 -0
  274. package/src/lab/artifacts/sanitize.ts +586 -0
  275. package/src/lab/artifacts/secure-fs.ts +475 -0
  276. package/src/lab/artifacts/store.ts +310 -0
  277. package/src/lab/automation/budgets.ts +78 -0
  278. package/src/lab/automation/config-persistence.ts +256 -0
  279. package/src/lab/automation/constants.ts +39 -0
  280. package/src/lab/automation/cooldown.ts +103 -0
  281. package/src/lab/automation/dispatch.ts +211 -0
  282. package/src/lab/automation/index.ts +13 -0
  283. package/src/lab/automation/orchestrator.ts +499 -0
  284. package/src/lab/automation/persistence.ts +512 -0
  285. package/src/lab/automation/planner.ts +371 -0
  286. package/src/lab/automation/policy.ts +136 -0
  287. package/src/lab/automation/queue.ts +191 -0
  288. package/src/lab/automation/recovery.ts +24 -0
  289. package/src/lab/automation/route-context.ts +21 -0
  290. package/src/lab/automation/run-key.ts +44 -0
  291. package/src/lab/automation/runs-query.ts +34 -0
  292. package/src/lab/automation/types.ts +160 -0
  293. package/src/lab/conformance/assertion.ts +325 -0
  294. package/src/lab/conformance/digest.ts +22 -0
  295. package/src/lab/conformance/executor.ts +741 -0
  296. package/src/lab/conformance/fixture-provider.ts +27 -0
  297. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  298. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  299. package/src/lab/conformance/harness-budget.ts +47 -0
  300. package/src/lab/conformance/index.ts +5 -0
  301. package/src/lab/conformance/jcs.ts +64 -0
  302. package/src/lab/conformance/json-pointer.ts +39 -0
  303. package/src/lab/conformance/manifest.ts +180 -0
  304. package/src/lab/conformance/mcp-stub.ts +179 -0
  305. package/src/lab/conformance/negative-controls.ts +164 -0
  306. package/src/lab/conformance/observation.ts +355 -0
  307. package/src/lab/conformance/runner.ts +68 -0
  308. package/src/lab/conformance/sse-normalize.ts +59 -0
  309. package/src/lab/conformance/suite-manifest.ts +78 -0
  310. package/src/lab/conformance/types.ts +214 -0
  311. package/src/lab/constants.ts +126 -0
  312. package/src/lab/digest.ts +64 -0
  313. package/src/lab/events/errors.ts +9 -0
  314. package/src/lab/events/limits.ts +117 -0
  315. package/src/lab/events/types.ts +229 -0
  316. package/src/lab/events/validate.ts +781 -0
  317. package/src/lab/fabric/constants.ts +40 -0
  318. package/src/lab/fabric/executor.ts +492 -0
  319. package/src/lab/fabric/index.ts +80 -0
  320. package/src/lab/fabric/manifest.ts +222 -0
  321. package/src/lab/fabric/observe.ts +489 -0
  322. package/src/lab/fabric/patch.ts +79 -0
  323. package/src/lab/fabric/producer-child.ts +139 -0
  324. package/src/lab/fabric/producer-isolate.ts +276 -0
  325. package/src/lab/fabric/producer-protocol.ts +61 -0
  326. package/src/lab/fabric/scratch.ts +439 -0
  327. package/src/lab/fabric/subject.ts +106 -0
  328. package/src/lab/fabric/types.ts +134 -0
  329. package/src/lab/fabric/verifier.ts +98 -0
  330. package/src/lab/index.ts +54 -0
  331. package/src/lab/ledger/artifact-refs.ts +127 -0
  332. package/src/lab/ledger/invalidation.ts +136 -0
  333. package/src/lab/ledger/purge.ts +310 -0
  334. package/src/lab/ledger/store.ts +532 -0
  335. package/src/lab/live/credential-lease.ts +53 -0
  336. package/src/lab/live/destination.ts +155 -0
  337. package/src/lab/live/executor.ts +336 -0
  338. package/src/lab/live/inert-tools.ts +56 -0
  339. package/src/lab/live/manifest.ts +85 -0
  340. package/src/lab/live/mcp-loopback.ts +57 -0
  341. package/src/lab/live/runner.ts +19 -0
  342. package/src/lab/live/sandbox.ts +61 -0
  343. package/src/lab/live/suite-manifest.ts +41 -0
  344. package/src/lab/live/transport.ts +118 -0
  345. package/src/lab/live/types.ts +197 -0
  346. package/src/lab/observe/from-conformance.ts +301 -0
  347. package/src/lab/observe/from-live.ts +117 -0
  348. package/src/lab/paths.ts +153 -0
  349. package/src/lab/projection/rebuild.ts +495 -0
  350. package/src/lab/projection/schema.ts +135 -0
  351. package/src/lab/projection/verdicts.ts +474 -0
  352. package/src/lab/projection/verification.ts +412 -0
  353. package/src/lab/public/bundle.ts +217 -0
  354. package/src/lab/public/community-authority.ts +175 -0
  355. package/src/lab/public/community-files.ts +29 -0
  356. package/src/lab/public/community.ts +479 -0
  357. package/src/lab/public/file-safety.ts +155 -0
  358. package/src/lab/public/ids.ts +26 -0
  359. package/src/lab/public/index.ts +16 -0
  360. package/src/lab/public/mutation-lock.ts +424 -0
  361. package/src/lab/public/operator.ts +353 -0
  362. package/src/lab/public/origin-purge.ts +79 -0
  363. package/src/lab/public/origin.ts +203 -0
  364. package/src/lab/public/privacy.ts +143 -0
  365. package/src/lab/public/private-file.ts +261 -0
  366. package/src/lab/public/project.ts +124 -0
  367. package/src/lab/public/purge-test-fault.ts +21 -0
  368. package/src/lab/public/purge.ts +223 -0
  369. package/src/lab/public/registry.ts +44 -0
  370. package/src/lab/public/revocation.ts +252 -0
  371. package/src/lab/public/signature.ts +243 -0
  372. package/src/lab/public/storage.ts +105 -0
  373. package/src/lab/public/strict-json.ts +206 -0
  374. package/src/lab/public/time.ts +26 -0
  375. package/src/lab/public/types.ts +172 -0
  376. package/src/lab/public/validate.ts +391 -0
  377. package/src/lab/query/catalog.ts +101 -0
  378. package/src/lab/query/connection.ts +107 -0
  379. package/src/lab/query/constants.ts +4 -0
  380. package/src/lab/query/cursor.ts +132 -0
  381. package/src/lab/query/dto-map.ts +277 -0
  382. package/src/lab/query/errors.ts +22 -0
  383. package/src/lab/query/freshness.ts +53 -0
  384. package/src/lab/query/index.ts +45 -0
  385. package/src/lab/query/latest-observation.ts +59 -0
  386. package/src/lab/query/passive-production.ts +159 -0
  387. package/src/lab/query/queries.ts +444 -0
  388. package/src/lab/query/types.ts +266 -0
  389. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  390. package/src/lab/subject/installation-salt.ts +112 -0
  391. package/src/lab/subject/protocol-subject.ts +80 -0
  392. package/src/lab/subject/route-subject.ts +74 -0
  393. package/src/lib/app-owned-memory-stores.ts +22 -0
  394. package/src/lib/bounded-body.ts +153 -9
  395. package/src/lib/bun-runtime.ts +125 -12
  396. package/src/lib/bun-stream-caps.ts +13 -9
  397. package/src/lib/codex-restart-contract.ts +120 -0
  398. package/src/lib/config-ownership.ts +6 -2
  399. package/src/lib/destination-policy.ts +65 -1
  400. package/src/lib/errors.ts +44 -2
  401. package/src/lib/fabric-task-execution-authority.ts +7 -0
  402. package/src/lib/fabric-task-host.ts +29 -0
  403. package/src/lib/lab-activation.ts +223 -0
  404. package/src/lib/lab-live-execution-authority.ts +13 -0
  405. package/src/lib/lab-live-host.ts +30 -0
  406. package/src/lib/lab-live-pinned-sender.ts +56 -0
  407. package/src/lib/lab-live-route-production.ts +130 -0
  408. package/src/lib/lab-passive-linker-registration.ts +26 -0
  409. package/src/lib/local-management-attestation.ts +51 -0
  410. package/src/lib/local-management-capability.ts +100 -0
  411. package/src/lib/local-provider-reload-contract.ts +100 -0
  412. package/src/lib/optional-shutdown-hooks.ts +57 -0
  413. package/src/lib/pinned-http.ts +145 -26
  414. package/src/lib/process-control.ts +4 -1
  415. package/src/lib/provider-outbound.ts +49 -9
  416. package/src/lib/redact.ts +419 -3
  417. package/src/lib/self-launch-argv.ts +15 -0
  418. package/src/lib/server-resource-ownership.ts +71 -0
  419. package/src/lib/shadow-call.ts +35 -4
  420. package/src/lib/sse-decoder.ts +41 -0
  421. package/src/lib/state-store-registrations.ts +10 -2
  422. package/src/lib/system-restart-contract.ts +73 -0
  423. package/src/lib/token-estimate.ts +19 -2
  424. package/src/lib/tool-argument-integers.ts +202 -0
  425. package/src/lib/translator-budget.ts +44 -0
  426. package/src/lib/upstream-http-version.ts +57 -0
  427. package/src/lib/upstream-reachability.ts +95 -0
  428. package/src/lib/upstream-retry.ts +156 -3
  429. package/src/lib/windows-atomic-replace.ts +155 -0
  430. package/src/lib/windows-elevation.ts +70 -2
  431. package/src/lib/windows-secret-acl.ts +409 -69
  432. package/src/lib/windows-service-wrappers.ts +72 -0
  433. package/src/lib/windows-text.ts +106 -0
  434. package/src/lib/windows-user-principal.ts +341 -0
  435. package/src/lib/winsw.ts +33 -5
  436. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  437. package/src/oauth/account-import/index.ts +15 -0
  438. package/src/oauth/account-import/parser.ts +83 -0
  439. package/src/oauth/account-import/registry.ts +18 -0
  440. package/src/oauth/account-import/service.ts +75 -0
  441. package/src/oauth/account-import/types.ts +91 -0
  442. package/src/oauth/anthropic.ts +12 -1
  443. package/src/oauth/callback-server.ts +8 -2
  444. package/src/oauth/chatgpt.ts +12 -1
  445. package/src/oauth/command-code.ts +239 -0
  446. package/src/oauth/cursor.ts +46 -5
  447. package/src/oauth/google-antigravity.ts +35 -3
  448. package/src/oauth/health.ts +20 -12
  449. package/src/oauth/index.ts +398 -66
  450. package/src/oauth/key-providers.ts +16 -0
  451. package/src/oauth/kimi.ts +16 -2
  452. package/src/oauth/kiro.ts +50 -6
  453. package/src/oauth/local-token-detect.ts +11 -2
  454. package/src/oauth/log.ts +3 -1
  455. package/src/oauth/login-cli.ts +88 -28
  456. package/src/oauth/nous.ts +798 -0
  457. package/src/oauth/store.ts +119 -21
  458. package/src/oauth/token-guardian.ts +9 -3
  459. package/src/pi/models.ts +2 -2
  460. package/src/providers/alibaba-region-migration.ts +1 -1
  461. package/src/providers/antigravity-models.ts +521 -31
  462. package/src/providers/base-url-choices.ts +10 -0
  463. package/src/providers/codex-capacity.ts +292 -0
  464. package/src/providers/command-code-efforts.ts +144 -0
  465. package/src/providers/context-cap.ts +22 -5
  466. package/src/providers/cursor-pool.ts +72 -0
  467. package/src/providers/derive.ts +253 -6
  468. package/src/providers/fastwire.ts +501 -0
  469. package/src/providers/free-directory.ts +10 -7
  470. package/src/providers/google-vertex-location.ts +14 -0
  471. package/src/providers/key-failover.ts +71 -3
  472. package/src/providers/label.ts +1 -1
  473. package/src/providers/model-discovery-limits.ts +16 -0
  474. package/src/providers/model-discovery.ts +115 -22
  475. package/src/providers/model-rename-migration.ts +255 -0
  476. package/src/providers/model-rename-startup.ts +28 -0
  477. package/src/providers/openai-sidecar.ts +72 -4
  478. package/src/providers/openai-tier-startup.ts +31 -2
  479. package/src/providers/openai-tiers.ts +119 -4
  480. package/src/providers/openai-virtual-models.ts +1 -0
  481. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  482. package/src/providers/provider-id-rewrite.ts +29 -0
  483. package/src/providers/quota.ts +1319 -38
  484. package/src/providers/registry.ts +1429 -111
  485. package/src/providers/request-pacing.ts +310 -0
  486. package/src/providers/service-tier.ts +277 -0
  487. package/src/providers/slug-codec.ts +42 -6
  488. package/src/providers/static-model-discovery.ts +86 -0
  489. package/src/providers/xai-responses-opt-in.ts +15 -0
  490. package/src/providers/xai-transport.ts +11 -4
  491. package/src/reasoning-effort.ts +49 -1
  492. package/src/responses/compaction.ts +26 -1
  493. package/src/responses/custom-tool-compat.ts +266 -0
  494. package/src/responses/hosted-tool-policy.ts +9 -0
  495. package/src/responses/namespace-tool-compat.ts +355 -0
  496. package/src/responses/parser.ts +220 -38
  497. package/src/responses/provider-continuation.ts +98 -0
  498. package/src/responses/provider-opaque-metadata.ts +73 -0
  499. package/src/responses/reasoning-envelope.ts +9 -1
  500. package/src/responses/reasoning-replay-cache.ts +426 -0
  501. package/src/responses/schema.ts +7 -1
  502. package/src/responses/spill-store.ts +75 -10
  503. package/src/responses/state.ts +565 -27
  504. package/src/responses/thought-signature-replay.ts +347 -0
  505. package/src/responses/tool-search-compat.ts +301 -0
  506. package/src/responses/truncated-stop-reason.ts +60 -0
  507. package/src/router.ts +366 -30
  508. package/src/routing/analytics.ts +378 -0
  509. package/src/routing/capability.ts +244 -0
  510. package/src/routing/compatibility/assemble.ts +73 -0
  511. package/src/routing/compatibility/behavior.ts +278 -0
  512. package/src/routing/compatibility/catalog.ts +99 -0
  513. package/src/routing/compatibility/endpoint.ts +52 -0
  514. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  515. package/src/routing/compatibility/policy.ts +181 -0
  516. package/src/routing/compatibility/provider-slot.ts +56 -0
  517. package/src/routing/compatibility/reader.ts +110 -0
  518. package/src/routing/compatibility/subject.ts +191 -0
  519. package/src/routing/compatibility/types.ts +64 -0
  520. package/src/routing/compatibility/version.ts +104 -0
  521. package/src/routing/cost.ts +77 -0
  522. package/src/routing/evaluator.ts +495 -0
  523. package/src/routing/health.ts +412 -0
  524. package/src/routing/history/cursor.ts +43 -0
  525. package/src/routing/history/indexer.ts +605 -0
  526. package/src/routing/history/schema.ts +72 -0
  527. package/src/routing/profile-namespace.ts +15 -0
  528. package/src/routing/profile.ts +547 -0
  529. package/src/routing/quota.ts +145 -0
  530. package/src/routing/request-evidence.ts +45 -0
  531. package/src/routing/trace.ts +776 -0
  532. package/src/server/adapter-resolve.ts +2 -29
  533. package/src/server/auth-cors.ts +267 -46
  534. package/src/server/background-lifecycle.ts +182 -0
  535. package/src/server/chat-completions.ts +130 -56
  536. package/src/server/chat-native-sse.ts +331 -0
  537. package/src/server/chat-native.ts +426 -0
  538. package/src/server/claude-messages.ts +159 -43
  539. package/src/server/direct-local-http.ts +347 -0
  540. package/src/server/effort-policy.ts +18 -0
  541. package/src/server/github-copilot-responses-repair.ts +338 -0
  542. package/src/server/gui-static.ts +39 -10
  543. package/src/server/images.ts +94 -12
  544. package/src/server/index.ts +865 -181
  545. package/src/server/lifecycle.ts +284 -13
  546. package/src/server/live.ts +136 -17
  547. package/src/server/local-management-read-client.ts +90 -0
  548. package/src/server/local-provider-reload-client.ts +137 -0
  549. package/src/server/management/agent-settings-routes.ts +398 -103
  550. package/src/server/management/api-key-usage.ts +31 -5
  551. package/src/server/management/body.ts +6 -0
  552. package/src/server/management/combo-routes.ts +62 -24
  553. package/src/server/management/config-routes.ts +464 -51
  554. package/src/server/management/context.ts +80 -2
  555. package/src/server/management/integration-routes.ts +498 -0
  556. package/src/server/management/lab-automation-routes.ts +206 -0
  557. package/src/server/management/lab-routes.ts +563 -0
  558. package/src/server/management/logs-usage-routes.ts +101 -32
  559. package/src/server/management/model-routes.ts +189 -131
  560. package/src/server/management/model-rows.ts +163 -0
  561. package/src/server/management/native-integration-routes.ts +769 -0
  562. package/src/server/management/oauth-account-routes.ts +80 -4
  563. package/src/server/management/provider-capability-config.ts +48 -0
  564. package/src/server/management/provider-routes.ts +764 -157
  565. package/src/server/management/request-history-routes.ts +191 -0
  566. package/src/server/management/routing-analytics-routes.ts +74 -0
  567. package/src/server/management/routing-profile-routes.ts +380 -0
  568. package/src/server/management/shared.ts +27 -11
  569. package/src/server/management/sidebar-routes.ts +47 -31
  570. package/src/server/management/storage-log-guard-routes.ts +186 -0
  571. package/src/server/management/sync-response.ts +69 -0
  572. package/src/server/management/system-restart.ts +288 -32
  573. package/src/server/management/system-routes.ts +77 -0
  574. package/src/server/management/usage-summary-cache.ts +9 -1
  575. package/src/server/management/vision-sidecar-options.ts +167 -0
  576. package/src/server/management/web-search-sidecar-options.ts +120 -0
  577. package/src/server/management-api.ts +115 -14
  578. package/src/server/management-auth.ts +220 -5
  579. package/src/server/passive-route-linker.ts +66 -0
  580. package/src/server/ports.ts +41 -1
  581. package/src/server/proxy-liveness.ts +132 -5
  582. package/src/server/readiness.ts +99 -0
  583. package/src/server/relay-eager.ts +82 -42
  584. package/src/server/relay.ts +236 -76
  585. package/src/server/request-decompress.ts +113 -6
  586. package/src/server/request-log.ts +235 -22
  587. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  588. package/src/server/responses/agent-task-recovery.ts +465 -0
  589. package/src/server/responses/collaboration.ts +204 -35
  590. package/src/server/responses/compact.ts +442 -55
  591. package/src/server/responses/core.ts +2872 -331
  592. package/src/server/responses/empty-completion-guard.ts +276 -0
  593. package/src/server/responses/encrypted-payload.ts +62 -39
  594. package/src/server/responses/fetch-helpers.ts +79 -4
  595. package/src/server/responses/input-admission.ts +185 -0
  596. package/src/server/responses/pacing-overload.ts +13 -0
  597. package/src/server/responses/policy-fallback.ts +178 -0
  598. package/src/server/responses/responses-field-backfill.ts +251 -0
  599. package/src/server/responses/terminal-guard.ts +26 -5
  600. package/src/server/responses/upstream-error.ts +5 -0
  601. package/src/server/responses/ws-upstream.ts +308 -0
  602. package/src/server/responses-custom-tool-repair.ts +282 -0
  603. package/src/server/responses-item-id-repair.ts +54 -6
  604. package/src/server/responses-json-events.ts +90 -0
  605. package/src/server/responses-model-rewrite.ts +29 -0
  606. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  607. package/src/server/responses-snapshot-repair.ts +621 -0
  608. package/src/server/responses-terminal-repair.ts +342 -0
  609. package/src/server/responses-tool-search-repair.ts +267 -0
  610. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  611. package/src/server/responses.ts +18 -2
  612. package/src/server/search.ts +78 -13
  613. package/src/server/sse-frame-buffer.ts +292 -0
  614. package/src/server/sse-payload-rewrite.ts +110 -22
  615. package/src/server/startup-action-control.ts +8 -1
  616. package/src/server/startup-health-cache.ts +19 -1
  617. package/src/server/system-env.ts +80 -9
  618. package/src/server/ws-bridge.ts +39 -38
  619. package/src/service-manager-probe.ts +892 -0
  620. package/src/service.ts +1111 -90
  621. package/src/sidecar/auth.ts +92 -0
  622. package/src/sidecar/candidates.ts +83 -0
  623. package/src/storage/cleanup.ts +2 -2
  624. package/src/storage/scanner.ts +1 -1
  625. package/src/storage/worker-lifecycle.ts +14 -14
  626. package/src/tray/windows-tray.ps1 +83 -9
  627. package/src/tray/windows.ts +43 -16
  628. package/src/types/accounts.ts +37 -0
  629. package/src/types/config.ts +845 -0
  630. package/src/types/provider.ts +545 -0
  631. package/src/types/request.ts +384 -0
  632. package/src/types/tools.ts +131 -0
  633. package/src/types/wire.ts +80 -0
  634. package/src/types.ts +104 -1236
  635. package/src/update/index.ts +32 -19
  636. package/src/update/job.ts +442 -67
  637. package/src/update/notify.ts +12 -6
  638. package/src/update/npm-cache-preflight.d.mts +47 -0
  639. package/src/update/npm-cache-preflight.mjs +201 -0
  640. package/src/update/transactional-install.d.mts +22 -0
  641. package/src/update/transactional-install.mjs +259 -0
  642. package/src/usage/cost.ts +0 -0
  643. package/src/usage/expected-prices.ts +268 -16
  644. package/src/usage/log.ts +606 -41
  645. package/src/usage/summary.ts +177 -9
  646. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  647. package/src/usage/user-cost-overlays.ts +314 -0
  648. package/src/vision/anthropic-describe.ts +10 -6
  649. package/src/vision/backends.ts +97 -0
  650. package/src/vision/describe.ts +9 -3
  651. package/src/vision/eligibility.ts +250 -0
  652. package/src/vision/index.ts +238 -24
  653. package/src/vision/reasoning.ts +55 -0
  654. package/src/vision/routed-describe.ts +175 -0
  655. package/src/vision/timeout-bounds.ts +9 -0
  656. package/src/web-search/anthropic-executor.ts +13 -7
  657. package/src/web-search/backends.ts +108 -0
  658. package/src/web-search/exa-executor.ts +88 -0
  659. package/src/web-search/executor.ts +11 -3
  660. package/src/web-search/gemini-executor.ts +141 -0
  661. package/src/web-search/index.ts +150 -15
  662. package/src/web-search/loop.ts +279 -50
  663. package/src/web-search/parse.ts +125 -30
  664. package/src/web-search/sources.ts +60 -0
  665. package/src/web-search/xai-executor.ts +219 -0
  666. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  667. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  668. package/gui/dist/provider-icons/antigravity.svg +0 -1
  669. package/gui/dist/provider-icons/claude.svg +0 -1
  670. package/gui/dist/provider-icons/copilot.svg +0 -1
  671. package/gui/dist/provider-icons/cursor.svg +0 -2
  672. package/gui/dist/provider-icons/gemini.svg +0 -1
  673. package/gui/dist/provider-icons/grok-color.svg +0 -1
  674. package/gui/dist/provider-icons/kiro.svg +0 -14
  675. package/src/cli/internal-dispatch.ts +0 -20
@@ -0,0 +1,426 @@
1
+ /**
2
+ * In-process fallback store pairing raw reasoning text with the tool call it
3
+ * preceded (issue #950).
4
+ *
5
+ * DeepSeek thinking mode requires the assistant's original `reasoning_content`
6
+ * to be replayed on every continuation of a tool-call turn. The bridge records
7
+ * the raw reasoning here when it closes a reasoning block and a tool call
8
+ * follows; the openai-chat adapter re-attaches it when a `tool_calls`
9
+ * assistant message is about to serialize without thinking parts (compacted
10
+ * history, lost assistant turn, orphan-repaired tool results).
11
+ *
12
+ * Entries require a conversation identity in addition to the call id:
13
+ * provider-generated ids like `call_1` are not globally unique, so an
14
+ * unscoped process-wide key would let one conversation's reasoning bleed into
15
+ * another when ids collide (CodeRabbit P1 on #971).
16
+ *
17
+ * Privacy: entries hold reasoning text in memory only — never logged,
18
+ * serialized, or exported. Bounded by entry count, total bytes, and TTL, so a
19
+ * long-lived proxy cannot grow without limit.
20
+ */
21
+
22
+ import { createHash, createHmac, randomBytes } from "node:crypto";
23
+ import type {
24
+ OcxProviderConfig,
25
+ OcxReasoningReplayIdentity,
26
+ OcxReasoningReplayScopeRef,
27
+ } from "../types";
28
+
29
+ const MAX_ENTRIES = 64;
30
+ const MAX_TOTAL_BYTES = 256 * 1024;
31
+ const TTL_MS = 60 * 60 * 1000;
32
+ const replayIdentityKey = randomBytes(32);
33
+ const CREDENTIAL_HEADER_NAMES = new Set([
34
+ "authorization",
35
+ "api-key",
36
+ "chatgpt-account-id",
37
+ "cookie",
38
+ "openai-organization",
39
+ "openai-project",
40
+ "proxy-authorization",
41
+ "x-api-key",
42
+ "x-api-token",
43
+ "x-auth-token",
44
+ "x-goog-api-key",
45
+ "x-openai-organization",
46
+ "x-openai-project",
47
+ ]);
48
+
49
+ interface CacheEntry {
50
+ text: string;
51
+ bytes: number;
52
+ at: number;
53
+ }
54
+
55
+ interface ServingIdentityEntry {
56
+ identity: string;
57
+ bytes: number;
58
+ at: number;
59
+ }
60
+
61
+ const entries = new Map<string, CacheEntry>();
62
+ const servingIdentities = new Map<string, ServingIdentityEntry>();
63
+ let totalBytes = 0;
64
+ let servingIdentityTotalBytes = 0;
65
+ let clockForTests: (() => number) | null = null;
66
+
67
+ const now = (): number => clockForTests?.() ?? Date.now();
68
+
69
+ function nonEmpty(value: unknown): value is string {
70
+ return typeof value === "string" && value.trim().length > 0;
71
+ }
72
+
73
+ type ReasoningReplayIdentityTuple = readonly [string, string, string, string, string];
74
+
75
+ function tupleForIdentity(
76
+ identity: Readonly<OcxReasoningReplayIdentity> | undefined,
77
+ ): ReasoningReplayIdentityTuple | undefined {
78
+ if (
79
+ !nonEmpty(identity?.providerName)
80
+ || !nonEmpty(identity?.providerDestinationIdentity)
81
+ || !nonEmpty(identity?.adapterName)
82
+ || !nonEmpty(identity?.modelId)
83
+ || !nonEmpty(identity?.credentialIdentity)
84
+ ) return undefined;
85
+ return [
86
+ identity.providerName,
87
+ identity.providerDestinationIdentity,
88
+ identity.adapterName,
89
+ identity.modelId,
90
+ identity.credentialIdentity,
91
+ ];
92
+ }
93
+
94
+ function tupleForServingIdentity(
95
+ identity: Readonly<OcxReasoningReplayIdentity> | undefined,
96
+ ): ReasoningReplayIdentityTuple | undefined {
97
+ if (
98
+ !nonEmpty(identity?.providerName)
99
+ || !nonEmpty(identity?.providerDestinationDurableIdentity)
100
+ || !nonEmpty(identity?.adapterName)
101
+ || !nonEmpty(identity?.modelId)
102
+ || !nonEmpty(identity?.credentialDurableIdentity)
103
+ ) return undefined;
104
+ return [
105
+ identity.providerName,
106
+ identity.providerDestinationDurableIdentity,
107
+ identity.adapterName,
108
+ identity.modelId,
109
+ identity.credentialDurableIdentity,
110
+ ];
111
+ }
112
+
113
+ function keyFor(callId: string, scope: OcxReasoningReplayScopeRef | undefined): string | undefined {
114
+ const identity = tupleForIdentity(scope?.current);
115
+ if (!nonEmpty(callId) || !nonEmpty(scope?.clientThreadId) || !identity) return undefined;
116
+ return JSON.stringify([
117
+ scope.clientThreadId,
118
+ ...identity,
119
+ callId,
120
+ ]);
121
+ }
122
+
123
+ function deleteServingIdentity(threadId: string): void {
124
+ const entry = servingIdentities.get(threadId);
125
+ if (!entry) return;
126
+ servingIdentities.delete(threadId);
127
+ servingIdentityTotalBytes -= entry.bytes;
128
+ }
129
+
130
+ function sweepExpiredServingIdentities(at: number): void {
131
+ for (const [threadId, entry] of servingIdentities) {
132
+ if (at - entry.at >= TTL_MS) deleteServingIdentity(threadId);
133
+ }
134
+ }
135
+
136
+ function servingIdentityFor(
137
+ scope: OcxReasoningReplayScopeRef | undefined,
138
+ ): { threadId: string; identity: string } | undefined {
139
+ const threadId = scope?.clientThreadId;
140
+ const identityTuple = tupleForServingIdentity(scope?.current);
141
+ if (!nonEmpty(threadId) || !identityTuple) return undefined;
142
+ return { threadId, identity: JSON.stringify(identityTuple) };
143
+ }
144
+
145
+ /**
146
+ * Compare this request's route with the last successfully serving route for its client thread.
147
+ * A live mismatch means replayed opaque reasoning was minted by another backend and must not be
148
+ * forwarded to this one. Comparison deliberately does not refresh or replace the recorded route:
149
+ * a failed candidate request did not serve the thread.
150
+ *
151
+ * Serving provenance uses restart-stable destination and credential dimensions so token
152
+ * generations and other volatile credential material cannot create false route changes. Missing
153
+ * durable identity, expired, or evicted state is deliberately unknown rather than a mismatch.
154
+ * This store is process-local, so a backend switch spanning a proxy restart is not detected.
155
+ */
156
+ export function reasoningReplayServingIdentityChanged(
157
+ scope: OcxReasoningReplayScopeRef | undefined,
158
+ ): boolean {
159
+ const current = servingIdentityFor(scope);
160
+ if (!current) return false;
161
+ const at = now();
162
+ sweepExpiredServingIdentities(at);
163
+ const previous = servingIdentities.get(current.threadId);
164
+ return previous !== undefined && previous.identity !== current.identity;
165
+ }
166
+
167
+ /** Record the route only after it has successfully served the client thread. */
168
+ export function commitReasoningReplayServingIdentity(
169
+ scope: OcxReasoningReplayScopeRef | undefined,
170
+ ): void {
171
+ const current = servingIdentityFor(scope);
172
+ if (!current) return;
173
+ const at = now();
174
+ sweepExpiredServingIdentities(at);
175
+ const previous = servingIdentities.get(current.threadId);
176
+ const { threadId, identity } = current;
177
+ const bytes = Buffer.byteLength(JSON.stringify([threadId, identity]), "utf8");
178
+ if (bytes > MAX_TOTAL_BYTES) {
179
+ deleteServingIdentity(threadId);
180
+ return;
181
+ }
182
+
183
+ if (previous) deleteServingIdentity(threadId);
184
+ servingIdentities.set(threadId, { identity, bytes, at });
185
+ servingIdentityTotalBytes += bytes;
186
+ while (
187
+ (servingIdentityTotalBytes > MAX_TOTAL_BYTES || servingIdentities.size > MAX_ENTRIES)
188
+ && servingIdentities.size > 1
189
+ ) {
190
+ let oldestThreadId: string | undefined;
191
+ let oldestAt = Infinity;
192
+ for (const [candidateThreadId, entry] of servingIdentities) {
193
+ if (entry.at < oldestAt) {
194
+ oldestAt = entry.at;
195
+ oldestThreadId = candidateThreadId;
196
+ }
197
+ }
198
+ if (oldestThreadId === undefined) break;
199
+ deleteServingIdentity(oldestThreadId);
200
+ }
201
+ }
202
+
203
+ function processLocalIdentity(domain: string, material: string): string {
204
+ return createHmac("sha256", replayIdentityKey)
205
+ .update(domain)
206
+ .update("\0")
207
+ .update(material)
208
+ .digest("hex");
209
+ }
210
+
211
+ function credentialHeaderOverrides(headers: Record<string, string> | undefined): [string, string][] {
212
+ return Object.entries(headers ?? {})
213
+ .filter(([name, value]) => CREDENTIAL_HEADER_NAMES.has(name.trim().toLowerCase()) && nonEmpty(value))
214
+ .map(([name, value]) => [name.trim().toLowerCase(), value] as [string, string])
215
+ .sort(([leftName, leftValue], [rightName, rightValue]) => (
216
+ leftName.localeCompare(rightName) || leftValue.localeCompare(rightValue)
217
+ ));
218
+ }
219
+
220
+ /** Produce a non-reversible process-local identity for an exact upstream destination. */
221
+ export function reasoningReplayDestinationIdentity(baseUrl: string | undefined): string | undefined {
222
+ if (!nonEmpty(baseUrl)) return undefined;
223
+ const canonical = baseUrl.trim().replace(/\/+$/, "");
224
+ return `destination:${processLocalIdentity("destination", canonical)}`;
225
+ }
226
+
227
+ /**
228
+ * The same destination identity, but stable across restarts.
229
+ *
230
+ * The process-local form above is keyed by `randomBytes(32)` minted at module load, which
231
+ * is correct for an in-memory cache and fatal for a durable one: every key would change on
232
+ * restart and the store would silently stop matching anything. A plain digest of the same
233
+ * canonical URL is equally non-reversible for this purpose — the input is a configured
234
+ * endpoint, not a secret — and needs no persisted salt or new on-disk state.
235
+ */
236
+ export function durableReplayDestinationIdentity(baseUrl: string | undefined): string | undefined {
237
+ if (!nonEmpty(baseUrl)) return undefined;
238
+ const canonical = baseUrl.trim().replace(/\/+$/, "");
239
+ return `destination:${createHash("sha256").update("destination\0").update(canonical).digest("hex")}`;
240
+ }
241
+
242
+ /**
243
+ * Restart-stable credential identity for the DURABLE thought-signature store (#1926).
244
+ *
245
+ * Unlike the destination, credential material may be secret (an API key), so a plain
246
+ * unsalted digest would turn the store file into an offline verifier for candidate keys.
247
+ * The identity is therefore an HMAC under a random salt persisted NEXT TO the store: the
248
+ * salt is not a secret escrow (it holds no credential material) but it makes every digest
249
+ * useless outside this installation. Full 256-bit output — no truncation.
250
+ *
251
+ * OAuth accounts use the persisted account-slot id (not the rotating token/generation):
252
+ * relinking a slot to a different upstream account keeps the id, but the upstream then
253
+ * validates signatures against the new credential and rejects stale ones — the same
254
+ * fail-closed backstop the destination identity relies on. Credential-scoped header
255
+ * overrides participate so two provider entries sharing one key but different
256
+ * authorization headers stay distinct, mirroring the process-local identity.
257
+ */
258
+ export function durableReplayCredentialIdentity(
259
+ kind: "key" | "oauth" | "codex",
260
+ material: string | undefined,
261
+ headers: Record<string, string> | undefined,
262
+ salt: Buffer | undefined,
263
+ ): string | undefined {
264
+ if (!nonEmpty(material) || !salt || salt.length < 16) return undefined;
265
+ const overrides = credentialHeaderOverrides(headers);
266
+ return `credential:${createHmac("sha256", salt)
267
+ .update(`credential\0${kind}\0`)
268
+ .update(JSON.stringify([material, overrides]))
269
+ .digest("hex")}`;
270
+ }
271
+
272
+ /** Produce a non-reversible process-local identity for credential material. */
273
+ export function reasoningReplayCredentialIdentity(
274
+ kind: "key" | "oauth" | "codex",
275
+ material: string | undefined,
276
+ headers?: Record<string, string>,
277
+ ): string | undefined {
278
+ if (!nonEmpty(material)) return undefined;
279
+ const overrides = credentialHeaderOverrides(headers);
280
+ return `${kind}:${processLocalIdentity(`credential:${kind}`, JSON.stringify([material, overrides]))}`;
281
+ }
282
+
283
+ /** Bind Codex forwarding to the effective bearer/account and selected physical pool slot. */
284
+ export function reasoningReplayCodexCredentialIdentity(args: {
285
+ authorization?: string | null;
286
+ chatgptAccountId?: string | null;
287
+ accountId?: string | null;
288
+ credentialGeneration?: number | string | null;
289
+ writerGeneration?: number | string | null;
290
+ headers?: Record<string, string>;
291
+ }): string | undefined {
292
+ const configuredAuthorization = credentialHeaderOverrides(args.headers)
293
+ .find(([name]) => name === "authorization")?.[1];
294
+ const authorization = nonEmpty(args.authorization) ? args.authorization : configuredAuthorization;
295
+ if (!authorization) return undefined;
296
+ const material = JSON.stringify([
297
+ authorization,
298
+ nonEmpty(args.chatgptAccountId) ? args.chatgptAccountId : "",
299
+ nonEmpty(args.accountId) ? args.accountId : "",
300
+ args.credentialGeneration === null || args.credentialGeneration === undefined
301
+ ? ""
302
+ : String(args.credentialGeneration),
303
+ args.writerGeneration === null || args.writerGeneration === undefined
304
+ ? ""
305
+ : String(args.writerGeneration),
306
+ ]);
307
+ return reasoningReplayCredentialIdentity("codex", material, args.headers);
308
+ }
309
+
310
+ /** Bind OAuth replay to one persisted credential slot and exact token generation. */
311
+ export function reasoningReplayOAuthCredentialIdentity(
312
+ snapshot: Readonly<{ accountId: string; generation: string }> | undefined,
313
+ headers?: Record<string, string>,
314
+ ): string | undefined {
315
+ if (!snapshot || !nonEmpty(snapshot.accountId) || !nonEmpty(snapshot.generation)) return undefined;
316
+ return reasoningReplayCredentialIdentity(
317
+ "oauth",
318
+ `${snapshot.accountId}\0${snapshot.generation}`,
319
+ headers,
320
+ );
321
+ }
322
+
323
+ /** Bind key-auth provider material without putting raw secrets in the replay key. */
324
+ export function reasoningReplayKeyCredentialIdentity(
325
+ provider: Pick<OcxProviderConfig, "apiKey" | "headers">,
326
+ ): string | undefined {
327
+ const apiKey = nonEmpty(provider.apiKey) ? provider.apiKey : undefined;
328
+ // Public/static headers do not establish a physical credential boundary.
329
+ // Header-only/key-optional providers therefore fail closed for replay.
330
+ if (!apiKey) return undefined;
331
+ return reasoningReplayCredentialIdentity("key", apiKey, provider.headers);
332
+ }
333
+
334
+ /** Replace only the holder snapshot so parsed-request copies observe rotations. */
335
+ export function bindReasoningReplayScope(
336
+ scope: OcxReasoningReplayScopeRef | undefined,
337
+ identity: OcxReasoningReplayIdentity | undefined,
338
+ ): void {
339
+ if (!scope) return;
340
+ if (!identity || !keyFor("binding-check", { ...scope, current: identity })) {
341
+ delete scope.current;
342
+ return;
343
+ }
344
+ scope.current = { ...identity };
345
+ }
346
+
347
+ /**
348
+ * Record the raw reasoning text that preceded the given tool call.
349
+ *
350
+ * Expired entries are swept on insert so the TTL bound holds even when a call
351
+ * id is never read again.
352
+ */
353
+ export function rememberReasoningForCall(
354
+ callId: string,
355
+ text: string,
356
+ scope?: OcxReasoningReplayScopeRef,
357
+ ): void {
358
+ // Never fall back to a process-wide namespace. Call ids are supplied by
359
+ // clients/providers and are therefore neither unique nor trustworthy; an
360
+ // unscoped entry could be recovered by an unrelated request that reuses the
361
+ // same id.
362
+ // Empty provider deltas are absence of new reasoning, not a request to erase a candidate.
363
+ const key = keyFor(callId, scope);
364
+ if (!key || typeof text !== "string" || text.length === 0) return;
365
+ const bytes = Buffer.byteLength(text, "utf8");
366
+ // A single entry larger than the whole budget would immediately evict itself.
367
+ if (bytes > MAX_TOTAL_BYTES) return;
368
+ const at = now();
369
+ // Delete every due entry first so expired reasoning cannot linger until a
370
+ // later peek or capacity eviction.
371
+ for (const [key, entry] of entries) {
372
+ if (at - entry.at >= TTL_MS) {
373
+ entries.delete(key);
374
+ totalBytes -= entry.bytes;
375
+ }
376
+ }
377
+ const previous = entries.get(key);
378
+ if (previous) totalBytes -= previous.bytes;
379
+ entries.set(key, { text, bytes, at });
380
+ totalBytes += bytes;
381
+ // Evict oldest-first until both caps hold (never evict the entry just written
382
+ // while it is the only one — the loop guards on size > 1).
383
+ while ((totalBytes > MAX_TOTAL_BYTES || entries.size > MAX_ENTRIES) && entries.size > 1) {
384
+ let oldestKey: string | undefined;
385
+ let oldestAt = Infinity;
386
+ for (const [candidateKey, entry] of entries) {
387
+ if (entry.at < oldestAt) {
388
+ oldestAt = entry.at;
389
+ oldestKey = candidateKey;
390
+ }
391
+ }
392
+ if (oldestKey === undefined) break;
393
+ const evicted = entries.get(oldestKey)!;
394
+ totalBytes -= evicted.bytes;
395
+ entries.delete(oldestKey);
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Read the recorded reasoning for a call id without removing it: retries after
401
+ * a failed continuation reuse the same fallback.
402
+ */
403
+ export function peekReasoningForCall(
404
+ callId: string,
405
+ scope?: OcxReasoningReplayScopeRef,
406
+ ): string | undefined {
407
+ const key = keyFor(callId, scope);
408
+ if (!key) return undefined;
409
+ const entry = entries.get(key);
410
+ if (!entry) return undefined;
411
+ if (now() - entry.at >= TTL_MS) {
412
+ entries.delete(key);
413
+ totalBytes -= entry.bytes;
414
+ return undefined;
415
+ }
416
+ return entry.text;
417
+ }
418
+
419
+ /** Test-only: reset the cache and optionally pin the clock. */
420
+ export function clearReasoningReplayCacheForTests(clock?: (() => number) | null): void {
421
+ entries.clear();
422
+ servingIdentities.clear();
423
+ totalBytes = 0;
424
+ servingIdentityTotalBytes = 0;
425
+ clockForTests = clock ?? null;
426
+ }
@@ -64,6 +64,12 @@ const functionCallItemSchema = z.object({
64
64
  name: z.string().min(1),
65
65
  namespace: z.string().optional(),
66
66
  arguments: z.string().optional(),
67
+ // Provider-opaque metadata that must survive the round trip verbatim (issue #1735). The shape
68
+ // is bounded on purpose: only the one nested key we round-trip is modeled, so an unexpected
69
+ // payload cannot ride through as arbitrary passthrough state.
70
+ extra_content: z.object({
71
+ google: z.object({ thought_signature: z.string().optional() }).optional(),
72
+ }).optional(),
67
73
  });
68
74
  const functionCallOutputItemSchema = z.object({
69
75
  type: z.literal("function_call_output"),
@@ -107,7 +113,7 @@ export const toolSchema = z.object({
107
113
  const builtinToolSchema = z.object({ type: z.string() }).loose();
108
114
 
109
115
  const hostedToolType = z.enum([
110
- "web_search_preview", "file_search", "computer_use_preview",
116
+ "web_search", "web_search_preview", "file_search", "computer_use_preview",
111
117
  "code_interpreter", "image_generation", "mcp",
112
118
  ]);
113
119
 
@@ -17,7 +17,8 @@ import {
17
17
  import { createHash, randomBytes } from "node:crypto";
18
18
  import { join } from "node:path";
19
19
  import { getConfigDir } from "../config";
20
- import { forgetHardenedSecretPath, hardenSecretDir, hardenSecretPath } from "../lib/windows-secret-acl";
20
+ import { forgetEphemeralSecretPath, forgetHardenedSecretPath, hardenSecretDir, hardenSecretPath } from "../lib/windows-secret-acl";
21
+ import { isValidProviderContinuationOwner } from "./provider-continuation";
21
22
  import type { OcxProviderContinuationState } from "../types";
22
23
 
23
24
  export const RESPONSE_SPILL_VERSION = 1;
@@ -34,7 +35,19 @@ export interface ResponseSpillPayload {
34
35
  version: 1;
35
36
  responseId: string;
36
37
  createdAt: number;
38
+ clientThreadId?: string;
37
39
  items: unknown[];
40
+ /**
41
+ * Index in `items` where the provider output begins, used by replay-overlap detection
42
+ * in state.ts. Optional so a payload written before this field still loads (it simply
43
+ * never authorizes a skip).
44
+ *
45
+ * Compatibility is FORWARD-ONLY: `validPayload` is a strict key allowlist, so a build
46
+ * predating this field rejects a payload carrying it as corrupt rather than ignoring
47
+ * it. Rolling back across this change invalidates spilled entries, which degrades to a
48
+ * replay miss — an already-handled path — not to corrupted live state.
49
+ */
50
+ providerOutputStart?: number;
38
51
  providers?: OcxProviderContinuationState;
39
52
  }
40
53
 
@@ -45,9 +58,29 @@ export interface ResponseSpillRef {
45
58
  payloadBytes: number;
46
59
  }
47
60
 
61
+ /**
62
+ * Hard ceiling for one spill payload, enforced BOTH at direct-spill admission
63
+ * (state.ts refuses to durably retain a larger candidate) and at replay read
64
+ * (below). 256 MiB keeps the replay transient under the process-wide
65
+ * APP_OWNED_WORST_CASE_PINNED_BYTES ceiling (512 MiB); without an admission
66
+ * ceiling the read ceiling would strand write-only spills on disk.
67
+ */
68
+ export const MAX_RESPONSE_SPILL_PAYLOAD_BYTES = 256 * 1024 * 1024;
69
+
70
+ let spillPayloadCapOverride: number | null = null;
71
+
72
+ /** Test-only: lower/restore the single-spill payload ceiling (null restores). */
73
+ export function setResponseSpillPayloadCapForTests(bytes: number | null): void {
74
+ spillPayloadCapOverride = bytes;
75
+ }
76
+
77
+ export function responseSpillPayloadCap(): number {
78
+ return spillPayloadCapOverride ?? MAX_RESPONSE_SPILL_PAYLOAD_BYTES;
79
+ }
80
+
48
81
  export type ResponseSpillReadResult =
49
82
  | { ok: true; payload: ResponseSpillPayload }
50
- | { ok: false; reason: "missing" | "corrupt" };
83
+ | { ok: false; reason: "missing" | "corrupt" | "too_large" };
51
84
 
52
85
  export interface ResponseSpillCleanupResult {
53
86
  scanned: number;
@@ -176,17 +209,28 @@ function closeFile(fd: number): void {
176
209
  record("close");
177
210
  }
178
211
 
179
- function unlink(path: string): void {
212
+ function unlink(path: string, ephemeral = false): void {
180
213
  try {
181
214
  if (spillIoForTest?.unlink) spillIoForTest.unlink(path);
182
215
  else unlinkSync(path);
183
- forgetHardenedSecretPath(path);
216
+ // Ephemeral release only for publish temps; stable spill files keep their
217
+ // destination-keyed timeout memos (anti-restall) and drop just the
218
+ // success memo for the now-deleted file.
219
+ if (ephemeral) forgetEphemeralSecretPath(path);
220
+ else forgetHardenedSecretPath(path);
184
221
  } catch (error) {
185
- if (isErrno(error, "ENOENT")) forgetHardenedSecretPath(path);
222
+ if (isErrno(error, "ENOENT")) {
223
+ if (ephemeral) forgetEphemeralSecretPath(path);
224
+ else forgetHardenedSecretPath(path);
225
+ }
186
226
  throw error;
187
227
  }
188
228
  }
189
229
 
230
+ function unlinkEphemeral(path: string): void {
231
+ unlink(path, true);
232
+ }
233
+
190
234
  function publishNoReplace(tempPath: string, destinationPath: string): void {
191
235
  try {
192
236
  if (spillIoForTest?.link) spillIoForTest.link(tempPath, destinationPath);
@@ -229,13 +273,26 @@ function validPayload(value: unknown, responseId: string): value is ResponseSpil
229
273
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
230
274
  const payload = value as Record<string, unknown>;
231
275
  const keys = Object.keys(payload);
232
- if (keys.some(key => !["version", "responseId", "createdAt", "items", "providers"].includes(key))) return false;
276
+ if (keys.some(key => !["version", "responseId", "createdAt", "clientThreadId", "items", "providerOutputStart", "providers"].includes(key))) return false;
233
277
  if (payload.version !== 1 || payload.responseId !== responseId) return false;
234
278
  if (typeof payload.createdAt !== "number" || !Number.isFinite(payload.createdAt)) return false;
279
+ if (payload.clientThreadId !== undefined
280
+ && (typeof payload.clientThreadId !== "string" || payload.clientThreadId.trim().length === 0)) return false;
235
281
  if (!Array.isArray(payload.items)) return false;
282
+ // A malformed boundary must degrade to "never skip", never to a bad index: reject the
283
+ // payload outright so materialization treats it as corrupt rather than trusting it.
284
+ if (payload.providerOutputStart !== undefined) {
285
+ const anchor = payload.providerOutputStart;
286
+ if (typeof anchor !== "number" || !Number.isSafeInteger(anchor)
287
+ || anchor < 0 || anchor > payload.items.length) return false;
288
+ }
236
289
  if (payload.providers !== undefined) {
237
290
  if (!payload.providers || typeof payload.providers !== "object" || Array.isArray(payload.providers)) return false;
238
- for (const providerState of Object.values(payload.providers)) {
291
+ const providers = payload.providers as Record<string, unknown>;
292
+ if (providers.__ocxOwner !== undefined
293
+ && !isValidProviderContinuationOwner(providers.__ocxOwner)) return false;
294
+ for (const [provider, providerState] of Object.entries(providers)) {
295
+ if (provider === "__ocxOwner") continue;
239
296
  if (!providerState || typeof providerState !== "object" || Array.isArray(providerState)) return false;
240
297
  }
241
298
  }
@@ -253,7 +310,9 @@ export function writeResponseSpillDurably(
253
310
  version: 1,
254
311
  responseId,
255
312
  createdAt: state.createdAt,
313
+ ...(state.clientThreadId ? { clientThreadId: state.clientThreadId } : {}),
256
314
  items: state.items,
315
+ ...(state.providerOutputStart !== undefined ? { providerOutputStart: state.providerOutputStart } : {}),
257
316
  ...(state.providers ? { providers: state.providers } : {}),
258
317
  };
259
318
  const serialized = JSON.stringify(payload);
@@ -284,7 +343,7 @@ export function writeResponseSpillDurably(
284
343
  try {
285
344
  publishNoReplace(publishTempPath, destinationPath);
286
345
  fsyncDirectoryBestEffort(dir);
287
- unlink(publishTempPath);
346
+ unlinkEphemeral(publishTempPath);
288
347
  tempPath = null;
289
348
  return { version: 1, fileName, digest, payloadBytes: bytes.byteLength };
290
349
  } catch (error) {
@@ -298,7 +357,7 @@ export function writeResponseSpillDurably(
298
357
  try { closeSync(fd); } catch { /* best effort */ }
299
358
  }
300
359
  if (tempPath) {
301
- try { unlink(tempPath); } catch { /* best effort */ }
360
+ try { unlinkEphemeral(tempPath); } catch { /* best effort */ }
302
361
  }
303
362
  throw new Error("Response spill write failed");
304
363
  }
@@ -306,6 +365,9 @@ export function writeResponseSpillDurably(
306
365
 
307
366
  export function readResponseSpill(responseId: string, ref: ResponseSpillRef): ResponseSpillReadResult {
308
367
  if (!validSpillRef(ref)) return { ok: false, reason: "corrupt" };
368
+ // Refuse before any read/parse: an oversized declared payload would otherwise
369
+ // materialize an unbounded transient (readFileSync + utf8 + JSON.parse).
370
+ if (ref.payloadBytes > responseSpillPayloadCap()) return { ok: false, reason: "too_large" };
309
371
  const match = OWNED_SPILL_NAME.exec(ref.fileName);
310
372
  if (!match
311
373
  || match[2] !== sha256(responseId).slice(0, 12)
@@ -376,7 +438,10 @@ export function recoverOrphanedResponseSpills(
376
438
  try { stat = lstatSync(path); } catch { continue; }
377
439
  if (!stat.isFile() || stat.isSymbolicLink() || Date.now() - stat.mtimeMs < graceMs) continue;
378
440
  try {
379
- unlink(path);
441
+ // Orphaned publish temps get the full ephemeral release; stable
442
+ // orphaned spills keep destination-keyed timeout memos.
443
+ if (isOwnedTemp) unlinkEphemeral(path);
444
+ else unlink(path);
380
445
  result.removed += 1;
381
446
  result.bytesRemoved += stat.size;
382
447
  } catch {