@bitkyc08/opencodex 2.80.0 → 2.82.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 (376) hide show
  1. package/README.md +3 -3
  2. package/bin/ocx.mjs +166 -73
  3. package/gui/dist/assets/App-Ds_qL7o8.js +54 -0
  4. package/gui/dist/assets/Tray-BqHF5Trd.js +1 -0
  5. package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
  6. package/gui/dist/assets/index-D3FBCNiP.js +94 -0
  7. package/gui/dist/assets/quota-summary-DTBC8n7-.js +1 -0
  8. package/gui/dist/index.html +2 -2
  9. package/gui/dist/provider-icons/claude-green.svg +1 -0
  10. package/package.json +3 -2
  11. package/src/adapters/anthropic-model-contract.ts +8 -2
  12. package/src/adapters/anthropic.ts +8 -3
  13. package/src/adapters/codebuddy/mcp-server.ts +2 -25
  14. package/src/adapters/codebuddy/tool-bridge.ts +1 -7
  15. package/src/adapters/coding-agent/mcp-server.ts +33 -0
  16. package/src/adapters/coding-agent/protocol.ts +122 -17
  17. package/src/adapters/coding-agent/tool-catalog.ts +75 -0
  18. package/src/adapters/coding-agent/turn.ts +101 -44
  19. package/src/adapters/cursor/catalog.ts +8 -0
  20. package/src/adapters/cursor/effort-map.ts +2 -0
  21. package/src/adapters/devin/live-models.ts +2 -0
  22. package/src/adapters/kiro-retry.ts +9 -4
  23. package/src/adapters/openai-chat/messages.ts +7 -6
  24. package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
  25. package/src/adapters/openai-responses/passthrough.ts +9 -4
  26. package/src/adapters/openai-responses/tool-output-recovery.ts +20 -0
  27. package/src/adapters/physical-send.ts +8 -5
  28. package/src/adapters/qoder/adapter.ts +41 -6
  29. package/src/adapters/qoder/scaffold-guard.ts +2 -3
  30. package/src/adapters/zed.ts +16 -1
  31. package/src/claude/agents-inject.ts +14 -3
  32. package/src/claude/context-windows.ts +19 -17
  33. package/src/claude/desktop-3p.ts +88 -12
  34. package/src/claude/desktop-profile.ts +2 -1
  35. package/src/claude/gateway-cache.ts +18 -1
  36. package/src/claude/inbound-cache-stabilize.ts +4 -9
  37. package/src/claude/inbound.ts +78 -3
  38. package/src/claude/intercept/cli-catalog.ts +3 -0
  39. package/src/claude/intercept/model-bindings.ts +1 -0
  40. package/src/claude/intercept/picker-ca-persistence.ts +7 -6
  41. package/src/claude/intercept/picker-listener.ts +71 -4
  42. package/src/claude/intercept/picker-models.ts +14 -6
  43. package/src/claude/intercept/settings.ts +3 -0
  44. package/src/claude/long-context.ts +66 -0
  45. package/src/claude/model-info.ts +19 -16
  46. package/src/claude/outbound.ts +94 -9
  47. package/src/claude/subagent-model.ts +40 -11
  48. package/src/cli/account-anthropic-threshold.ts +14 -5
  49. package/src/cli/account-api.ts +3 -1
  50. package/src/cli/account-extended.ts +13 -9
  51. package/src/cli/account-policy-dto.ts +1 -1
  52. package/src/cli/account-policy.ts +13 -4
  53. package/src/cli/account.ts +3 -3
  54. package/src/cli/agent.ts +9 -5
  55. package/src/cli/aside-profile-recovery.ts +33 -0
  56. package/src/cli/capabilities-accounts.ts +2 -2
  57. package/src/cli/capabilities-agents-routing.ts +24 -0
  58. package/src/cli/capabilities-base.ts +5 -5
  59. package/src/cli/capabilities-integrations.ts +18 -2
  60. package/src/cli/cli-path-diagnostics.ts +132 -0
  61. package/src/cli/codex-shim-autorestore.ts +3 -0
  62. package/src/cli/desktop-runtime-guidance.ts +65 -0
  63. package/src/cli/dispatch.ts +15 -1
  64. package/src/cli/doctor.ts +16 -3
  65. package/src/cli/help.ts +4 -1
  66. package/src/cli/index.ts +25 -14
  67. package/src/cli/integration-aside-sync.ts +2 -1
  68. package/src/cli/integration-plan-dto.ts +1 -0
  69. package/src/cli/integration-preview.ts +8 -1
  70. package/src/cli/integrations.ts +48 -6
  71. package/src/cli/message-args.ts +34 -0
  72. package/src/cli/message-command.ts +43 -0
  73. package/src/cli/message-runtime.ts +6 -0
  74. package/src/cli/observe.ts +3 -1
  75. package/src/cli/provider.ts +29 -6
  76. package/src/cli/registry.ts +24 -2
  77. package/src/cli/resolve.ts +25 -2
  78. package/src/cli/root.ts +4 -2
  79. package/src/cli/status.ts +64 -12
  80. package/src/cli/update-restart-child.ts +4 -2
  81. package/src/cli/update-restart-home.ts +28 -10
  82. package/src/cli/update-restart-service-record.ts +116 -0
  83. package/src/cli/update-restart-supervision.ts +96 -0
  84. package/src/cli/update-restart.ts +59 -13
  85. package/src/cli/v2.ts +27 -20
  86. package/src/cli/version-skew-notice.ts +77 -0
  87. package/src/clients/config-export/commandcode.ts +131 -0
  88. package/src/clients/config-export/contracts.ts +12 -0
  89. package/src/clients/config-export.ts +64 -1
  90. package/src/clients/lazycodex.ts +1 -1
  91. package/src/clients/omo-role-models.ts +32 -7
  92. package/src/codex/auth-api/account-list.ts +11 -3
  93. package/src/codex/auth-api/account-selection.ts +107 -0
  94. package/src/codex/auth-api/main-account-probe.ts +25 -8
  95. package/src/codex/auth-api/pool-mode-gate.ts +33 -9
  96. package/src/codex/auth-api/pool-quota-probe.ts +15 -1
  97. package/src/codex/auth-api/routes.ts +11 -49
  98. package/src/codex/auth-context.ts +20 -1
  99. package/src/codex/autostart-health.ts +12 -3
  100. package/src/codex/catalog/auto-review.ts +13 -6
  101. package/src/codex/catalog/build-entries.ts +33 -7
  102. package/src/codex/catalog/bundled.ts +155 -26
  103. package/src/codex/catalog/combo-member.ts +3 -2
  104. package/src/codex/catalog/control-plane.ts +45 -0
  105. package/src/codex/catalog/effort.ts +37 -6
  106. package/src/codex/catalog/gather-capture.ts +10 -3
  107. package/src/codex/catalog/model-hints.ts +15 -8
  108. package/src/codex/catalog/model-visibility.ts +3 -1
  109. package/src/codex/catalog/operator-tiers.ts +38 -0
  110. package/src/codex/catalog/parsing.ts +2 -1
  111. package/src/codex/catalog/provider-models.ts +58 -44
  112. package/src/codex/catalog/retained-sync.ts +7 -5
  113. package/src/codex/catalog/routed-gather.ts +15 -1
  114. package/src/codex/catalog-auto-refresh-sources.ts +3 -4
  115. package/src/codex/catalog-auto-refresh.ts +19 -0
  116. package/src/codex/catalog.ts +1 -1
  117. package/src/codex/codex-home-owner.ts +3 -0
  118. package/src/codex/config-write-lock.ts +333 -0
  119. package/src/codex/control-plane-models.ts +6 -0
  120. package/src/codex/convergence.ts +2 -1
  121. package/src/codex/features.ts +257 -200
  122. package/src/codex/inject/config-write-section.ts +34 -0
  123. package/src/codex/inject/multi-agent-v2.ts +14 -6
  124. package/src/codex/inject/remove.ts +36 -11
  125. package/src/codex/inject/restore.ts +85 -20
  126. package/src/codex/inject-coordination.ts +15 -6
  127. package/src/codex/inject.ts +62 -20
  128. package/src/codex/journal.ts +62 -17
  129. package/src/codex/main-account-cache.ts +43 -0
  130. package/src/codex/paths.ts +2 -0
  131. package/src/codex/prepared-home.ts +18 -0
  132. package/src/codex/prompt-journal.ts +25 -12
  133. package/src/codex/prompt-layers.ts +49 -26
  134. package/src/codex/prompt-lock-claim.ts +134 -0
  135. package/src/codex/prompt-lock-evidence.ts +26 -0
  136. package/src/codex/prompt-lock-io.ts +16 -0
  137. package/src/codex/prompt-lock-owner.ts +130 -0
  138. package/src/codex/prompt-lock.ts +84 -53
  139. package/src/codex/routing/failure-window.ts +146 -0
  140. package/src/codex/routing/selection.ts +4 -1
  141. package/src/codex/routing.ts +13 -17
  142. package/src/codex/runtime.ts +106 -18
  143. package/src/codex/shim.ts +45 -19
  144. package/src/combos/jev-decision-contract.ts +25 -2
  145. package/src/combos/jev-service-exchange.ts +281 -0
  146. package/src/combos/jev.ts +34 -219
  147. package/src/combos/types.ts +1 -1
  148. package/src/config/atomic-write.ts +1 -1
  149. package/src/config/codex-ws-reuse-setting.ts +10 -0
  150. package/src/config/diagnostics.ts +27 -16
  151. package/src/config/live-reconcile.ts +17 -0
  152. package/src/config/load-degrade.ts +42 -0
  153. package/src/config/rebase-provenance.ts +11 -0
  154. package/src/config/schema/anthropic-account-pool.ts +143 -0
  155. package/src/config/schema/config-schema.ts +11 -1
  156. package/src/config/schema/leaf-validators.ts +4 -0
  157. package/src/config.ts +2 -0
  158. package/src/generated/compatibility-version.json +566 -298
  159. package/src/generated/model-metadata.ts +7 -6
  160. package/src/grok/inject.ts +50 -4
  161. package/src/grok/sync.ts +2 -1
  162. package/src/images/loop.ts +30 -11
  163. package/src/integrations/aside-profiles.ts +10 -3
  164. package/src/integrations/catalog-refresh.ts +19 -3
  165. package/src/integrations/mutation-plan.ts +7 -0
  166. package/src/integrations/registry.ts +8 -0
  167. package/src/integrations/state.ts +9 -2
  168. package/src/integrations/writer.ts +17 -5
  169. package/src/lib/bun-path-runtime.d.mts +32 -0
  170. package/src/lib/bun-path-runtime.mjs +95 -0
  171. package/src/lib/bun-runtime-preflight.ts +140 -0
  172. package/src/lib/desktop-cli-handoff.d.mts +20 -0
  173. package/src/lib/desktop-cli-handoff.mjs +92 -0
  174. package/src/lib/desktop-cli-record.d.mts +29 -0
  175. package/src/lib/desktop-cli-record.mjs +102 -0
  176. package/src/lib/lab-live-route-production.ts +9 -1
  177. package/src/lib/request-execution-budget.ts +244 -58
  178. package/src/lib/spend-pool-alias-validation.ts +79 -0
  179. package/src/lib/spend-pool-continuity.ts +34 -0
  180. package/src/lib/spend-reservation-ledger.ts +412 -50
  181. package/src/lib/state-store-registrations.ts +20 -3
  182. package/src/lib/synced-state-location.ts +46 -6
  183. package/src/lib/test-home-guard.ts +87 -1
  184. package/src/lib/translator-budget.ts +17 -4
  185. package/src/lib/upstream-retry.ts +17 -5
  186. package/src/lib/windows-atomic-replace.ts +38 -16
  187. package/src/lib/windows-elevation.ts +6 -0
  188. package/src/lib/windows-secret-acl.ts +92 -25
  189. package/src/lib/winsw.ts +2 -3
  190. package/src/lib/workflow-budget.ts +53 -11
  191. package/src/messaging/budget.ts +45 -0
  192. package/src/messaging/discovery.ts +57 -0
  193. package/src/messaging/envelope.ts +49 -0
  194. package/src/messaging/input.ts +37 -0
  195. package/src/messaging/rpc.ts +196 -0
  196. package/src/messaging/send.ts +58 -0
  197. package/src/messaging/socket.ts +72 -0
  198. package/src/messaging/types.ts +29 -0
  199. package/src/oauth/anthropic-account-refusal.ts +90 -18
  200. package/src/oauth/anthropic-account-threshold.ts +8 -2
  201. package/src/oauth/anthropic-continuity.ts +15 -1
  202. package/src/oauth/anthropic-model-quota.ts +120 -48
  203. package/src/oauth/anthropic-model-routes.ts +11 -3
  204. package/src/oauth/anthropic-oauth-definitions.ts +98 -0
  205. package/src/oauth/anthropic-pool-config.ts +30 -0
  206. package/src/oauth/anthropic-rate-limit-policy.ts +64 -9
  207. package/src/oauth/anthropic-routing.ts +1202 -866
  208. package/src/oauth/anthropic-send-ownership.ts +41 -0
  209. package/src/oauth/anthropic.ts +4 -1
  210. package/src/oauth/chatgpt.ts +113 -41
  211. package/src/oauth/generic-account-failover.ts +3 -1
  212. package/src/oauth/health.ts +8 -6
  213. package/src/oauth/index.ts +96 -175
  214. package/src/oauth/kiro.ts +22 -9
  215. package/src/oauth/login-cli.ts +2 -0
  216. package/src/oauth/model-discovery-auth.ts +43 -0
  217. package/src/oauth/pool-kernel.ts +11 -5
  218. package/src/oauth/pool-settings-capability.ts +5 -3
  219. package/src/oauth/store-anthropic-instance.ts +87 -0
  220. package/src/oauth/store.ts +68 -10
  221. package/src/oauth/token-guardian.ts +2 -0
  222. package/src/oauth/types.ts +2 -0
  223. package/src/protocols/plan-snapshot.ts +5 -0
  224. package/src/protocols/settings.ts +20 -5
  225. package/src/providers/anthropic-instance-id.ts +47 -0
  226. package/src/providers/anthropic-instance.ts +48 -0
  227. package/src/providers/anthropic-reset-grant-ledger.ts +11 -1
  228. package/src/providers/azure-model-metadata.ts +115 -0
  229. package/src/providers/derive.ts +23 -0
  230. package/src/providers/kiro-models.ts +3 -0
  231. package/src/providers/label.ts +1 -1
  232. package/src/providers/model-rename-fields.ts +2 -0
  233. package/src/providers/quota/account-cache.ts +37 -11
  234. package/src/providers/quota/anthropic-account-quota.ts +93 -0
  235. package/src/providers/quota/anthropic-cooldown-recovery.ts +222 -111
  236. package/src/providers/quota/report-cache.ts +24 -1
  237. package/src/providers/quota/vendor-probes-key.ts +127 -17
  238. package/src/providers/quota/vendor-probes-oauth.ts +46 -14
  239. package/src/providers/quota.ts +23 -33
  240. package/src/providers/registry/entries-core.ts +35 -30
  241. package/src/providers/registry/model-ids.ts +1 -0
  242. package/src/providers/registry/model-seeds.ts +7 -4
  243. package/src/providers/registry/types.ts +3 -0
  244. package/src/providers/registry.ts +3 -3
  245. package/src/responses/reasoning-envelope.ts +29 -1
  246. package/src/responses/reasoning-replay-cache.ts +17 -1
  247. package/src/responses/spill-store.ts +20 -3
  248. package/src/responses/state/metrics.ts +95 -0
  249. package/src/responses/state/snapshot-codec.ts +6 -0
  250. package/src/responses/state/spill-failure.ts +12 -0
  251. package/src/responses/state/spill-queue.ts +13 -5
  252. package/src/responses/state/unforced-store-false.ts +55 -0
  253. package/src/responses/state.ts +68 -76
  254. package/src/responses/task-input.ts +17 -0
  255. package/src/router.ts +28 -4
  256. package/src/routing/compatibility/assemble.ts +1 -1
  257. package/src/routing/quota.ts +7 -3
  258. package/src/server/auth-cors.ts +5 -0
  259. package/src/server/background-lifecycle.ts +27 -0
  260. package/src/server/chat-completions.ts +7 -1
  261. package/src/server/chat-native.ts +64 -28
  262. package/src/server/claude-messages.ts +59 -8
  263. package/src/server/gui-static.ts +38 -4
  264. package/src/server/inbound-body-admission.ts +1 -0
  265. package/src/server/index/claude-intercept-lifecycle.ts +3 -1
  266. package/src/server/index/serve-options.ts +10 -2
  267. package/src/server/index/spend-ledger-lifecycle.ts +12 -5
  268. package/src/server/index/startup-warnings.ts +19 -0
  269. package/src/server/index.ts +5 -2
  270. package/src/server/inference/context.ts +1 -1
  271. package/src/server/lifecycle.ts +1 -1
  272. package/src/server/management/agent-settings-routes.ts +123 -38
  273. package/src/server/management/anthropic-account-threshold.ts +12 -9
  274. package/src/server/management/anthropic-pool-settings.ts +72 -0
  275. package/src/server/management/anthropic-reset-grant-routes.ts +98 -39
  276. package/src/server/management/codex-agent-role-routes.ts +4 -2
  277. package/src/server/management/codex-prompt-routes.ts +1 -0
  278. package/src/server/management/codex-role-auto-assign.ts +13 -2
  279. package/src/server/management/config-routes.ts +85 -14
  280. package/src/server/management/context.ts +2 -2
  281. package/src/server/management/decision-routes.ts +2 -1
  282. package/src/server/management/integration-routes.ts +25 -2
  283. package/src/server/management/oauth-account-routes.ts +79 -98
  284. package/src/server/management/provider-instance-ownership.ts +44 -0
  285. package/src/server/management/provider-routes.ts +21 -28
  286. package/src/server/management/route-registry.ts +3 -1
  287. package/src/server/management/routing-profile-routes.ts +4 -4
  288. package/src/server/management/shared.ts +3 -3
  289. package/src/server/management/system-restart.ts +44 -14
  290. package/src/server/management/system-routes.ts +24 -2
  291. package/src/server/management/vision-sidecar-options.ts +9 -6
  292. package/src/server/management/web-search-sidecar-options.ts +51 -2
  293. package/src/server/management-auth.ts +6 -0
  294. package/src/server/messages-native-eligibility.ts +3 -2
  295. package/src/server/messages-native-oauth.ts +91 -45
  296. package/src/server/messages-native-selector.ts +39 -0
  297. package/src/server/messages-native.ts +133 -38
  298. package/src/server/messages-response-headers.ts +14 -0
  299. package/src/server/request-log-conversation.ts +48 -5
  300. package/src/server/request-log.ts +36 -2
  301. package/src/server/responses/adapter-continuation.ts +31 -19
  302. package/src/server/responses/adapter-dispatch.ts +75 -24
  303. package/src/server/responses/antigravity-validation-refusal.ts +20 -6
  304. package/src/server/responses/codex-ws-exchange.ts +17 -2
  305. package/src/server/responses/codex-ws-pool.ts +100 -13
  306. package/src/server/responses/codex-ws-wire.ts +9 -0
  307. package/src/server/responses/combo-requested-effort.ts +3 -1
  308. package/src/server/responses/compact.ts +25 -8
  309. package/src/server/responses/compaction-recovery.ts +1 -1
  310. package/src/server/responses/core-codex-account.ts +22 -1
  311. package/src/server/responses/core-combo-native.ts +24 -4
  312. package/src/server/responses/core-combo.ts +38 -25
  313. package/src/server/responses/core-lifetime.ts +3 -36
  314. package/src/server/responses/core-normalize.ts +1 -0
  315. package/src/server/responses/core-options.ts +8 -1
  316. package/src/server/responses/core-replay.ts +78 -0
  317. package/src/server/responses/core.ts +3 -3
  318. package/src/server/responses/fetch-helpers.ts +26 -3
  319. package/src/server/responses/passthrough-delivery.ts +30 -4
  320. package/src/server/responses/passthrough-dispatch.ts +39 -9
  321. package/src/server/responses/request-prepare.ts +41 -5
  322. package/src/server/responses/request-send-budget.ts +150 -50
  323. package/src/server/responses/request-spend.ts +325 -33
  324. package/src/server/responses/request-transport.ts +132 -52
  325. package/src/server/responses/response-effects.ts +1 -0
  326. package/src/server/responses/run-turn-execution.ts +13 -3
  327. package/src/server/responses/sidecar-execution.ts +113 -22
  328. package/src/server/responses/sidecar-send-budget.ts +58 -0
  329. package/src/server/responses/ws-upstream.ts +19 -6
  330. package/src/server/responses-undeclared-tool-guard.ts +5 -1
  331. package/src/server/startup-health-cache.ts +2 -1
  332. package/src/server/workflow-refusal.ts +33 -0
  333. package/src/service/cli.ts +20 -4
  334. package/src/service/desktop-command-guard.ts +32 -0
  335. package/src/service/desktop-startup.ts +66 -40
  336. package/src/service/desktop-supervision.d.mts +26 -0
  337. package/src/service/desktop-supervision.mjs +140 -0
  338. package/src/service/managing-cli.ts +1 -0
  339. package/src/service/orchestration.ts +46 -20
  340. package/src/service/ownership-mutation-lease.d.mts +1 -1
  341. package/src/service/ownership-mutation-lease.mjs +5 -7
  342. package/src/service/repair.ts +25 -9
  343. package/src/service/state.ts +4 -3
  344. package/src/service/systemd.ts +17 -8
  345. package/src/service/windows-ops.ts +61 -22
  346. package/src/sidecar/anthropic-binding.ts +79 -0
  347. package/src/sidecar/auth.ts +76 -2
  348. package/src/storage/policy-job.ts +5 -1
  349. package/src/storage/policy-worker.ts +3 -1
  350. package/src/storage/policy.ts +9 -0
  351. package/src/types/anthropic-account-pool.ts +33 -0
  352. package/src/types/config.ts +33 -28
  353. package/src/types/provider.ts +5 -0
  354. package/src/types/request.ts +5 -0
  355. package/src/types/tools.ts +7 -0
  356. package/src/types/wire.ts +1 -0
  357. package/src/types.ts +4 -0
  358. package/src/update/index.ts +57 -9
  359. package/src/update/restart-ownership.ts +10 -3
  360. package/src/update/runtime-ownership.d.mts +6 -0
  361. package/src/update/runtime-ownership.mjs +23 -7
  362. package/src/usage/cost.ts +3 -2
  363. package/src/usage/expected-prices.ts +66 -14
  364. package/src/vision/anthropic-describe.ts +16 -3
  365. package/src/vision/index.ts +3 -0
  366. package/src/vision/plan.ts +19 -6
  367. package/src/web-search/alpha-search.ts +7 -1
  368. package/src/web-search/anthropic-executor.ts +16 -4
  369. package/src/web-search/index.ts +7 -3
  370. package/src/web-search/loop.ts +32 -7
  371. package/src/web-search/passthrough-bridge.ts +4 -1
  372. package/src/web-search/sidecar-providers.ts +4 -3
  373. package/gui/dist/assets/App-MbDVfUeC.js +0 -54
  374. package/gui/dist/assets/Tray-CzkxK82A.js +0 -1
  375. package/gui/dist/assets/index-UJt6npXk.js +0 -94
  376. package/gui/dist/assets/quota-summary-DU3sD6Z-.js +0 -1
@@ -72,6 +72,7 @@ export interface ResponseSpillPayload {
72
72
  */
73
73
  providerOutputStart?: number;
74
74
  providers?: OcxProviderContinuationState;
75
+ unforcedStoreFalse?: boolean;
75
76
  }
76
77
 
77
78
  export interface ResponseSpillRef {
@@ -496,6 +497,7 @@ function serializedSpill(
496
497
  items: state.items,
497
498
  ...(state.providerOutputStart !== undefined ? { providerOutputStart: state.providerOutputStart } : {}),
498
499
  ...(state.providers ? { providers: state.providers } : {}),
500
+ ...(state.unforcedStoreFalse ? { unforcedStoreFalse: state.unforcedStoreFalse } : {}),
499
501
  };
500
502
  const serialized = JSON.stringify(payload);
501
503
  if (serialized === undefined) throw new Error("Response spill serialization failed");
@@ -549,12 +551,14 @@ function validPayload(value: unknown, responseId: string): value is ResponseSpil
549
551
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
550
552
  const payload = value as Record<string, unknown>;
551
553
  const keys = Object.keys(payload);
552
- if (keys.some(key => !["version", "responseId", "createdAt", "clientThreadId", "items", "providerOutputStart", "providers"].includes(key))) return false;
554
+ if (keys.some(key => !["version", "responseId", "createdAt", "clientThreadId", "items", "providerOutputStart", "providers", "unforcedStoreFalse"].includes(key))) return false;
553
555
  if (payload.version !== 1 || payload.responseId !== responseId) return false;
554
556
  if (typeof payload.createdAt !== "number" || !Number.isFinite(payload.createdAt)) return false;
555
557
  if (payload.clientThreadId !== undefined
556
558
  && (typeof payload.clientThreadId !== "string" || payload.clientThreadId.trim().length === 0)) return false;
559
+ if (payload.unforcedStoreFalse !== undefined && typeof payload.unforcedStoreFalse !== "boolean") return false;
557
560
  if (!Array.isArray(payload.items)) return false;
561
+ if (payload.unforcedStoreFalse === true && payload.providerOutputStart === undefined) return false;
558
562
  // A malformed boundary must degrade to "never skip", never to a bad index: reject the
559
563
  // payload outright so materialization treats it as corrupt rather than trusting it.
560
564
  if (payload.providerOutputStart !== undefined) {
@@ -761,10 +765,23 @@ export function readResponseSpill(responseId: string, ref: ResponseSpillRef): Re
761
765
  export function deleteResponseSpill(ref: ResponseSpillRef): void {
762
766
  if (!validSpillRef(ref)) return;
763
767
  const dir = responseSpillDirectory();
768
+ const path = join(dir, ref.fileName);
764
769
  try {
765
- unlink(join(dir, ref.fileName));
770
+ unlink(path);
766
771
  fsyncDirectoryBestEffort(dir);
767
- } catch { /* best effort */ }
772
+ } catch (error) {
773
+ // Still best effort, but no longer invisible: a file a failed unlink left behind occupies
774
+ // the volume, so the accounting owner keeps pricing it until the path is gone (#6747).
775
+ if (!isErrno(error, "ENOENT")) spillUnlinkFailureObserver?.(path, ref.payloadBytes);
776
+ }
777
+ }
778
+
779
+ type SpillUnlinkFailureObserver = (path: string, bytes: number) => void;
780
+ let spillUnlinkFailureObserver: SpillUnlinkFailureObserver | null = null;
781
+
782
+ /** The owner of spill accounting registers here so a failed unlink cannot under-report disk use. */
783
+ export function setResponseSpillUnlinkFailureObserver(next: SpillUnlinkFailureObserver | null): void {
784
+ spillUnlinkFailureObserver = next;
768
785
  }
769
786
 
770
787
  type SpillDirNameKind = "spill" | "temp";
@@ -0,0 +1,95 @@
1
+ import type { StoredResponseState } from "../state";
2
+ import type { ResponseSpillWriteStatus, ResponseSpillWriteFailureCode, ResponseSpillWriteFailureOrigin } from "./spill-failure";
3
+
4
+ export interface ResponseStateMetrics {
5
+ count: number;
6
+ residentCount: number;
7
+ spillStubCount: number;
8
+ tombstoneCount: number;
9
+ totalBytes: number;
10
+ spillPayloadBytes: number;
11
+ largestBytes: number;
12
+ oldestAgeMs: number;
13
+ spillWrites: number;
14
+ spillWriteFailures: number;
15
+ spillWriteStatus: ResponseSpillWriteStatus;
16
+ spillWriteConsecutiveFailures: number;
17
+ spillLastWriteFailureCode: ResponseSpillWriteFailureCode | null;
18
+ spillLastWriteFailureOrigin: ResponseSpillWriteFailureOrigin | null;
19
+ spillAclRetryReturnedTimeouts: number;
20
+ spillAclTimeoutMemoRefusals: number;
21
+ /** Publications refused because the durable spill cap could not make room (cumulative). */
22
+ spillCapacityRefusals: number;
23
+ /** Spills evicted by admission to make room for a newer publication (cumulative). */
24
+ spillHeadroomEvictions: number;
25
+ spillLastWriteFailureAt: number | null;
26
+ spillLastWriteSuccessAt: number | null;
27
+ spillReadFailures: number;
28
+ replayScopeMismatchDrops: number;
29
+ }
30
+
31
+ export function computeResponseStateMetrics(params: {
32
+ states: Iterable<StoredResponseState>;
33
+ stateCount: number;
34
+ now: number;
35
+ totalBytes: number;
36
+ spillWrites: number;
37
+ spillWriteFailures: number;
38
+ spillWriteConsecutiveFailures: number;
39
+ spillLastFailureCode: ResponseSpillWriteFailureCode | null;
40
+ spillLastFailureOrigin: ResponseSpillWriteFailureOrigin | null;
41
+ spillLastSuccessAt: number | null;
42
+ spillAclRetryReturnedTimeouts: number;
43
+ spillAclTimeoutMemoRefusals: number;
44
+ spillCapacityRefusals: number;
45
+ spillHeadroomEvictions: number;
46
+ spillLastFailureAt: number | null;
47
+ spillReadFailures: number;
48
+ replayScopeMismatchDrops: number;
49
+ }): ResponseStateMetrics {
50
+ let largestBytes = 0;
51
+ let oldestCreatedAt = params.now;
52
+ let residentCount = 0;
53
+ let spillStubCount = 0;
54
+ let tombstoneCount = 0;
55
+ let spillPayloadBytes = 0;
56
+ for (const state of params.states) {
57
+ const bytes = state.sizeBytes;
58
+ if (bytes > largestBytes) largestBytes = bytes;
59
+ if (state.createdAt < oldestCreatedAt) oldestCreatedAt = state.createdAt;
60
+ if (state.kind === "resident") {
61
+ residentCount += 1;
62
+ } else if (state.kind === "spill") {
63
+ spillStubCount += 1;
64
+ spillPayloadBytes += state.spill.payloadBytes;
65
+ } else tombstoneCount += 1;
66
+ }
67
+ return {
68
+ count: params.stateCount,
69
+ residentCount,
70
+ spillStubCount,
71
+ tombstoneCount,
72
+ totalBytes: params.totalBytes,
73
+ spillPayloadBytes,
74
+ largestBytes,
75
+ oldestAgeMs: params.stateCount > 0 ? params.now - oldestCreatedAt : 0,
76
+ spillWrites: params.spillWrites,
77
+ spillWriteFailures: params.spillWriteFailures,
78
+ spillWriteStatus: params.spillWriteConsecutiveFailures > 0
79
+ ? "degraded"
80
+ : params.spillLastSuccessAt !== null
81
+ ? "healthy"
82
+ : "initial",
83
+ spillWriteConsecutiveFailures: params.spillWriteConsecutiveFailures,
84
+ spillLastWriteFailureCode: params.spillLastFailureCode,
85
+ spillLastWriteFailureOrigin: params.spillLastFailureOrigin,
86
+ spillAclRetryReturnedTimeouts: params.spillAclRetryReturnedTimeouts,
87
+ spillAclTimeoutMemoRefusals: params.spillAclTimeoutMemoRefusals,
88
+ spillCapacityRefusals: params.spillCapacityRefusals,
89
+ spillHeadroomEvictions: params.spillHeadroomEvictions,
90
+ spillLastWriteFailureAt: params.spillLastFailureAt,
91
+ spillLastWriteSuccessAt: params.spillLastSuccessAt,
92
+ spillReadFailures: params.spillReadFailures,
93
+ replayScopeMismatchDrops: params.replayScopeMismatchDrops,
94
+ };
95
+ }
@@ -24,6 +24,7 @@ interface LegacySnapshotState {
24
24
  providers?: OcxProviderContinuationState;
25
25
  conversationId?: unknown;
26
26
  cursorCheckpointUsable?: unknown;
27
+ unforcedStoreFalse?: unknown;
27
28
  }
28
29
 
29
30
  function isSpillRef(value: unknown): value is ResponseSpillRef {
@@ -51,6 +52,9 @@ export function loadSnapshotEntry(id: string, value: unknown, store: SnapshotLoa
51
52
  ? raw as number
52
53
  : undefined;
53
54
  };
55
+ // Restricted retention requires proven provider output; legacy unrestricted rows stay readable.
56
+ if (rec.unforcedStoreFalse === true
57
+ && anchorFor(rec.kind === "spill" ? Number.MAX_SAFE_INTEGER : Array.isArray(rec.items) ? rec.items.length : 0) === undefined) return;
54
58
  if (rec.kind === "spill") {
55
59
  if (!isSpillRef(rec.spill)) return;
56
60
  const base: Omit<SpilledResponseState, "sizeBytes"> = {
@@ -61,6 +65,7 @@ export function loadSnapshotEntry(id: string, value: unknown, store: SnapshotLoa
61
65
  // here; the spill payload validator re-checks it against the real array.
62
66
  ...(anchorFor(Number.MAX_SAFE_INTEGER) !== undefined ? { providerOutputStart: anchorFor(Number.MAX_SAFE_INTEGER) } : {}),
63
67
  ...(rec.providers ? { providers: rec.providers } : {}),
68
+ ...(rec.unforcedStoreFalse === true ? { unforcedStoreFalse: true } : {}),
64
69
  spill: rec.spill,
65
70
  };
66
71
  store.replaceMapEntry(id, { ...base, sizeBytes: store.stubSize(id, base) });
@@ -88,6 +93,7 @@ export function loadSnapshotEntry(id: string, value: unknown, store: SnapshotLoa
88
93
  items: rec.items,
89
94
  ...(anchorFor(rec.items.length) !== undefined ? { providerOutputStart: anchorFor(rec.items.length) } : {}),
90
95
  ...(providers ? { providers } : {}),
96
+ ...(rec.unforcedStoreFalse === true ? { unforcedStoreFalse: true } : {}),
91
97
  });
92
98
  if (!resident) {
93
99
  store.replaceMapEntry(id, store.tombstone(id, rec.createdAt));
@@ -1,8 +1,14 @@
1
1
  export const spillCounters = {
2
2
  writes: 0, writeFailures: 0, readFailures: 0,
3
3
  aclRetryReturnedTimeouts: 0, aclTimeoutMemoRefusals: 0,
4
+ capacityRefusals: 0, headroomEvictions: 0,
4
5
  };
5
6
 
7
+ /** Test-only: zero every spill counter, including ones added later. */
8
+ export function resetSpillCountersForTests(): void {
9
+ for (const key of Object.keys(spillCounters) as Array<keyof typeof spillCounters>) spillCounters[key] = 0;
10
+ }
11
+
6
12
  export type ResponseSpillWriteFailureCode =
7
13
  | "EACLRETRYEXHAUSTED"
8
14
  | "ETIMEDOUT"
@@ -102,6 +108,12 @@ export function noteSpillWriteFailure(
102
108
  if (origin === "retry_returned_timeout") spillCounters.aclRetryReturnedTimeouts += 1;
103
109
  else if (origin === "timeout_memo_refusal") spillCounters.aclTimeoutMemoRefusals += 1;
104
110
  }
111
+
112
+ /** A publication refused because the durable spill cap could not make room for it (#6747). */
113
+ export function noteSpillCapacityRefusal(): void {
114
+ spillCounters.capacityRefusals += 1;
115
+ noteSpillWriteFailure(null, "ECAPACITY");
116
+ }
105
117
  /**
106
118
  * Admission-boundary observability (test-visible). directSpills: oversized
107
119
  * candidates routed straight to durable spill without a resident stay or
@@ -11,12 +11,14 @@ import {
11
11
  responseSpillNow,
12
12
  type ResponseSpillPublicationControl,
13
13
  type ResponseSpillRef,
14
+ setResponseSpillUnlinkFailureObserver,
14
15
  writeResponseSpillDurably,
15
16
  writeResponseSpillDurablyAsync,
16
17
  } from "../spill-store";
17
18
  import { enforceAppOwnedMemoryBudget } from "../../lib/app-owned-memory";
18
19
  import {
19
20
  admissionCounters,
21
+ noteSpillCapacityRefusal,
20
22
  noteSpillWriteFailure,
21
23
  noteSpillWriteSuccess,
22
24
  spillAclMemoRefusalOrigin,
@@ -42,7 +44,8 @@ export interface SpillQueueStore {
42
44
  pruneResponses(): void;
43
45
  accountedResponseSpillBytes(): number;
44
46
  spillByteCap(): number;
45
- enforceSpilledResponseBudget(): number;
47
+ /** No argument: plain cap pruning. A number (0 included): admission reclaim of that headroom. */
48
+ enforceSpilledResponseBudget(requiredHeadroomBytes?: number): number;
46
49
  terminalizationMaxPasses(): number;
47
50
  }
48
51
 
@@ -122,6 +125,8 @@ function chargeUnreclaimableSpillPath(path: string | null | undefined, bytes: nu
122
125
  if (!path || bytes <= 0) return;
123
126
  unreclaimableSpillPaths.set(path, bytes);
124
127
  }
128
+ // A failed unlink of an installed or deferred spill leaves a real file too; price it the same way.
129
+ setResponseSpillUnlinkFailureObserver(chargeUnreclaimableSpillPath);
125
130
 
126
131
  /** Drop charges for paths that have since disappeared; returns the surviving total. */
127
132
  function reconcileUnreclaimableSpillPaths(): number {
@@ -191,6 +196,7 @@ function spillPayloadForResident(candidate: ResidentResponseState): Parameters<t
191
196
  items: candidate.items,
192
197
  ...(candidate.providerOutputStart !== undefined ? { providerOutputStart: candidate.providerOutputStart } : {}),
193
198
  ...(candidate.providers ? { providers: candidate.providers } : {}),
199
+ ...(candidate.unforcedStoreFalse ? { unforcedStoreFalse: candidate.unforcedStoreFalse } : {}),
194
200
  };
195
201
  }
196
202
 
@@ -285,9 +291,10 @@ export function queuePendingResponseSpill(
285
291
  // against a total that is short by a whole envelope.
286
292
  const inheritedBytes = inheritedSpill?.payloadBytes ?? 0;
287
293
  if (requireStore().accountedResponseSpillBytes() + footprint + inheritedBytes > requireStore().spillByteCap()) {
288
- requireStore().enforceSpilledResponseBudget();
294
+ // An envelope over the replay ceiling ends in EFBIG anyway; never evict a continuation for it.
295
+ if (footprint / 2 <= responseSpillPayloadCap()) requireStore().enforceSpilledResponseBudget(footprint + inheritedBytes);
289
296
  if (requireStore().accountedResponseSpillBytes() + footprint + inheritedBytes > requireStore().spillByteCap()) {
290
- noteSpillWriteFailure(null, "ECAPACITY");
297
+ noteSpillCapacityRefusal();
291
298
  requireStore().replaceWithSpillFailure(id, candidate);
292
299
  requireStore().deferSupersededSpill(inheritedSpill);
293
300
  return;
@@ -420,10 +427,11 @@ function installShutdownFallbackSpill(
420
427
  // over budget. `replaceWithSpillFailure` is the same fail-closed ending the budget
421
428
  // exhaustion path uses, so replay reports `spill_failed` and the client resends.
422
429
  if (requireStore().accountedResponseSpillBytes() + supersededBytes > requireStore().spillByteCap()) {
423
- requireStore().enforceSpilledResponseBudget();
430
+ // The footprint is already reserved above, so only the superseded generation is extra room.
431
+ if (footprint / 2 <= responseSpillPayloadCap()) requireStore().enforceSpilledResponseBudget(supersededBytes);
424
432
  if (requireStore().accountedResponseSpillBytes() + supersededBytes > requireStore().spillByteCap()) {
425
433
  if (requireStore().currentEntry(job.id) === candidate) {
426
- noteSpillWriteFailure(null, "ECAPACITY");
434
+ noteSpillCapacityRefusal();
427
435
  requireStore().replaceWithSpillFailure(job.id, candidate);
428
436
  requireStore().deferSupersededSpill(job.supersededSpill);
429
437
  }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Closed provenance and replay gating for unforced store:false client tool call responses.
3
+ *
4
+ * An unforced store:false response is cached exclusively to allow the client to send a matching
5
+ * function/custom tool output continuation on the next turn. Normal replay, text continuation, or
6
+ * mismatched output without a matching pending call fails replay and requires the client to
7
+ * supply the full conversation.
8
+ */
9
+
10
+ export function hasPendingClientToolCall(output: readonly unknown[]): boolean {
11
+ return output.some(item =>
12
+ !!item && typeof item === "object" && clientToolOutputType((item as { type?: unknown }).type) !== undefined,
13
+ );
14
+ }
15
+
16
+ export function allowsUnforcedStoreFalseReplay(
17
+ stored: readonly unknown[],
18
+ providerOutputStart: number | undefined,
19
+ clientInput: readonly unknown[],
20
+ carried: number,
21
+ ): boolean {
22
+ if (typeof providerOutputStart !== "number" || !Number.isSafeInteger(providerOutputStart)
23
+ || providerOutputStart < 0 || providerOutputStart > stored.length) return false;
24
+ const anchor = providerOutputStart;
25
+ const pendingCallIds = new Map<string, string>();
26
+ for (const item of stored.slice(anchor)) {
27
+ if (item && typeof item === "object") {
28
+ const outputType = clientToolOutputType((item as { type?: unknown }).type);
29
+ const callId = (item as { call_id?: unknown }).call_id;
30
+ if (outputType && typeof callId === "string" && callId) pendingCallIds.set(callId, outputType);
31
+ }
32
+ }
33
+ for (let i = 0; i < carried; i++) {
34
+ const item = clientInput[i];
35
+ if (item && typeof item === "object") {
36
+ const callId = (item as { call_id?: unknown }).call_id;
37
+ if (typeof callId === "string" && pendingCallIds.get(callId) === (item as { type?: unknown }).type) pendingCallIds.delete(callId);
38
+ }
39
+ }
40
+ return clientInput.slice(carried).some(item => {
41
+ if (item && typeof item === "object") {
42
+ const callId = (item as { call_id?: unknown }).call_id;
43
+ return typeof callId === "string" && pendingCallIds.has(callId)
44
+ && pendingCallIds.get(callId) === (item as { type?: unknown }).type;
45
+ }
46
+ return false;
47
+ });
48
+ }
49
+
50
+ /** Closed call/output pairs supported by the client-owned continuation path. */
51
+ function clientToolOutputType(type: unknown): string | undefined {
52
+ if (type === "function_call") return "function_call_output";
53
+ if (type === "custom_tool_call") return "custom_tool_call_output";
54
+ return undefined;
55
+ }