codex-multi-auth 2.17.2 → 2.17.3

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 (394) hide show
  1. package/.codex-plugin/plugin.json +1 -1
  2. package/README.md +83 -428
  3. package/dist/index.d.ts +0 -1
  4. package/dist/lib/account-policy.d.ts +18 -2
  5. package/dist/lib/account-policy.js +97 -40
  6. package/dist/lib/accounts/rate-limits.d.ts +0 -1
  7. package/dist/lib/accounts.d.ts +0 -1
  8. package/dist/lib/accounts.js +4 -5
  9. package/dist/lib/api-route-store.d.ts +0 -1
  10. package/dist/lib/audit.d.ts +0 -1
  11. package/dist/lib/audit.js +43 -12
  12. package/dist/lib/auth/account-access.d.ts +0 -1
  13. package/dist/lib/auth/account-access.js +5 -1
  14. package/dist/lib/auth/auth.d.ts +4 -3
  15. package/dist/lib/auth/auth.js +220 -28
  16. package/dist/lib/auth/browser.d.ts +0 -1
  17. package/dist/lib/auth/callback-guidance.d.ts +0 -1
  18. package/dist/lib/auth/device-auth.d.ts +0 -1
  19. package/dist/lib/auth/index.d.ts +0 -1
  20. package/dist/lib/auth/org-override.d.ts +0 -1
  21. package/dist/lib/auth/server.d.ts +1 -2
  22. package/dist/lib/auth/server.js +100 -53
  23. package/dist/lib/auth/token-utils.d.ts +0 -1
  24. package/dist/lib/auth-rate-limit.d.ts +0 -1
  25. package/dist/lib/budget-guard.d.ts +16 -2
  26. package/dist/lib/budget-guard.js +123 -43
  27. package/dist/lib/capability-policy.d.ts +0 -1
  28. package/dist/lib/circuit-breaker.d.ts +20 -1
  29. package/dist/lib/circuit-breaker.js +24 -5
  30. package/dist/lib/cli.d.ts +0 -1
  31. package/dist/lib/codex-cli/index.d.ts +0 -1
  32. package/dist/lib/codex-cli/observability.d.ts +0 -1
  33. package/dist/lib/codex-cli/state.d.ts +0 -1
  34. package/dist/lib/codex-cli/sync.d.ts +0 -1
  35. package/dist/lib/codex-cli/writer.d.ts +0 -1
  36. package/dist/lib/codex-manager/account-credentials.d.ts +0 -1
  37. package/dist/lib/codex-manager/account-manager-commands.d.ts +0 -1
  38. package/dist/lib/codex-manager/account-pool-write.d.ts +0 -1
  39. package/dist/lib/codex-manager/active-account-sync.d.ts +6 -0
  40. package/dist/lib/codex-manager/active-account-sync.js +336 -0
  41. package/dist/lib/codex-manager/api-login-menu.d.ts +0 -1
  42. package/dist/lib/codex-manager/backend-category-entry.d.ts +0 -1
  43. package/dist/lib/codex-manager/backend-category-helpers.d.ts +0 -1
  44. package/dist/lib/codex-manager/backend-category-prompt.d.ts +0 -1
  45. package/dist/lib/codex-manager/backend-settings-controller.d.ts +0 -1
  46. package/dist/lib/codex-manager/backend-settings-entry.d.ts +0 -1
  47. package/dist/lib/codex-manager/backend-settings-helpers.d.ts +0 -1
  48. package/dist/lib/codex-manager/backend-settings-prompt.d.ts +0 -1
  49. package/dist/lib/codex-manager/backend-settings-schema.d.ts +0 -1
  50. package/dist/lib/codex-manager/behavior-settings-panel.d.ts +0 -1
  51. package/dist/lib/codex-manager/commands/account.d.ts +8 -3
  52. package/dist/lib/codex-manager/commands/account.js +46 -34
  53. package/dist/lib/codex-manager/commands/best.d.ts +0 -1
  54. package/dist/lib/codex-manager/commands/bridge.d.ts +0 -1
  55. package/dist/lib/codex-manager/commands/budget.d.ts +8 -3
  56. package/dist/lib/codex-manager/commands/budget.js +6 -4
  57. package/dist/lib/codex-manager/commands/check.d.ts +0 -1
  58. package/dist/lib/codex-manager/commands/config-explain.d.ts +0 -1
  59. package/dist/lib/codex-manager/commands/debug-bundle.d.ts +0 -1
  60. package/dist/lib/codex-manager/commands/forecast.d.ts +0 -1
  61. package/dist/lib/codex-manager/commands/history.d.ts +0 -1
  62. package/dist/lib/codex-manager/commands/init-config.d.ts +0 -1
  63. package/dist/lib/codex-manager/commands/integrations.d.ts +0 -1
  64. package/dist/lib/codex-manager/commands/limits.d.ts +0 -1
  65. package/dist/lib/codex-manager/commands/models.d.ts +0 -1
  66. package/dist/lib/codex-manager/commands/monitor.d.ts +0 -1
  67. package/dist/lib/codex-manager/commands/report.d.ts +0 -1
  68. package/dist/lib/codex-manager/commands/resets.d.ts +0 -1
  69. package/dist/lib/codex-manager/commands/resets.js +9 -1
  70. package/dist/lib/codex-manager/commands/rotation.d.ts +0 -1
  71. package/dist/lib/codex-manager/commands/status.d.ts +0 -1
  72. package/dist/lib/codex-manager/commands/switch.d.ts +0 -1
  73. package/dist/lib/codex-manager/commands/uninstall.d.ts +0 -1
  74. package/dist/lib/codex-manager/commands/unpin.d.ts +0 -1
  75. package/dist/lib/codex-manager/commands/usage.d.ts +0 -1
  76. package/dist/lib/codex-manager/commands/verify.d.ts +0 -1
  77. package/dist/lib/codex-manager/commands/why-selected.d.ts +0 -1
  78. package/dist/lib/codex-manager/commands/workspace.d.ts +0 -1
  79. package/dist/lib/codex-manager/dashboard-display-panel.d.ts +0 -1
  80. package/dist/lib/codex-manager/dashboard-formatters.d.ts +0 -1
  81. package/dist/lib/codex-manager/dashboard-settings-controller.d.ts +0 -1
  82. package/dist/lib/codex-manager/dashboard-settings-data.d.ts +0 -1
  83. package/dist/lib/codex-manager/dashboard-settings-entry.d.ts +0 -1
  84. package/dist/lib/codex-manager/experimental-settings-entry.d.ts +0 -1
  85. package/dist/lib/codex-manager/experimental-settings-prompt.d.ts +0 -1
  86. package/dist/lib/codex-manager/experimental-settings-schema.d.ts +0 -1
  87. package/dist/lib/codex-manager/experimental-sync-target-entry.d.ts +0 -1
  88. package/dist/lib/codex-manager/experimental-sync-target.d.ts +0 -1
  89. package/dist/lib/codex-manager/forecast-report-shared.d.ts +0 -1
  90. package/dist/lib/codex-manager/formatters/account-formatters.d.ts +0 -1
  91. package/dist/lib/codex-manager/formatters/index.d.ts +0 -1
  92. package/dist/lib/codex-manager/formatters/model-formatters.d.ts +0 -1
  93. package/dist/lib/codex-manager/formatters/quota-formatters.d.ts +0 -1
  94. package/dist/lib/codex-manager/formatters/text-style.d.ts +0 -1
  95. package/dist/lib/codex-manager/health-check.d.ts +0 -1
  96. package/dist/lib/codex-manager/help.d.ts +0 -1
  97. package/dist/lib/codex-manager/login-action-panel.d.ts +0 -1
  98. package/dist/lib/codex-manager/login-flow.d.ts +0 -1
  99. package/dist/lib/codex-manager/login-menu-actions.d.ts +0 -1
  100. package/dist/lib/codex-manager/login-menu-data.d.ts +0 -1
  101. package/dist/lib/codex-manager/login-oauth.d.ts +0 -1
  102. package/dist/lib/codex-manager/login-workspace-choice.d.ts +0 -1
  103. package/dist/lib/codex-manager/manual-callback.d.ts +0 -1
  104. package/dist/lib/codex-manager/persist-selected-account.d.ts +0 -1
  105. package/dist/lib/codex-manager/quota-cache-helpers.d.ts +0 -1
  106. package/dist/lib/codex-manager/rate-limit-markers.d.ts +0 -1
  107. package/dist/lib/codex-manager/repair-commands.d.ts +0 -1
  108. package/dist/lib/codex-manager/settings-hub/backend.d.ts +0 -1
  109. package/dist/lib/codex-manager/settings-hub/dashboard.d.ts +0 -1
  110. package/dist/lib/codex-manager/settings-hub/experimental.d.ts +0 -1
  111. package/dist/lib/codex-manager/settings-hub/index.d.ts +0 -1
  112. package/dist/lib/codex-manager/settings-hub/shared.d.ts +0 -1
  113. package/dist/lib/codex-manager/settings-hub-entry.d.ts +0 -1
  114. package/dist/lib/codex-manager/settings-hub-menu.d.ts +0 -1
  115. package/dist/lib/codex-manager/settings-hub-prompt.d.ts +0 -1
  116. package/dist/lib/codex-manager/settings-hub.d.ts +0 -1
  117. package/dist/lib/codex-manager/settings-panels.d.ts +0 -1
  118. package/dist/lib/codex-manager/settings-persist-utils.d.ts +0 -1
  119. package/dist/lib/codex-manager/settings-preview.d.ts +0 -1
  120. package/dist/lib/codex-manager/settings-write-queue.d.ts +0 -1
  121. package/dist/lib/codex-manager/statusline-settings-panel.d.ts +0 -1
  122. package/dist/lib/codex-manager/theme-settings-panel.d.ts +0 -1
  123. package/dist/lib/codex-manager/unified-settings-controller.d.ts +0 -1
  124. package/dist/lib/codex-manager/unified-settings-entry.d.ts +0 -1
  125. package/dist/lib/codex-manager.d.ts +1 -2
  126. package/dist/lib/codex-manager.js +5 -95
  127. package/dist/lib/concurrency.d.ts +0 -1
  128. package/dist/lib/config-schema.d.ts +0 -1
  129. package/dist/lib/config.d.ts +0 -1
  130. package/dist/lib/config.js +46 -241
  131. package/dist/lib/constants.d.ts +0 -1
  132. package/dist/lib/context-budget/model-context-windows.d.ts +0 -1
  133. package/dist/lib/context-budget-guard.d.ts +0 -1
  134. package/dist/lib/context-budget-response.d.ts +0 -1
  135. package/dist/lib/context-overflow.d.ts +0 -1
  136. package/dist/lib/dashboard-settings.d.ts +0 -1
  137. package/dist/lib/entitlement-cache.d.ts +0 -1
  138. package/dist/lib/env-parsing.d.ts +13 -1
  139. package/dist/lib/env-parsing.js +21 -0
  140. package/dist/lib/errors.d.ts +0 -1
  141. package/dist/lib/forecast.d.ts +0 -1
  142. package/dist/lib/fs-retry.d.ts +0 -1
  143. package/dist/lib/health.d.ts +0 -1
  144. package/dist/lib/index.d.ts +0 -1
  145. package/dist/lib/integration-generators.d.ts +0 -1
  146. package/dist/lib/live-account-sync.d.ts +0 -1
  147. package/dist/lib/local-bridge.d.ts +0 -1
  148. package/dist/lib/local-bridge.js +4 -1
  149. package/dist/lib/local-client-tokens.d.ts +0 -1
  150. package/dist/lib/local-client-tokens.js +125 -30
  151. package/dist/lib/logger.d.ts +0 -1
  152. package/dist/lib/logger.js +11 -1
  153. package/dist/lib/model-capability-matrix.d.ts +0 -1
  154. package/dist/lib/model-route-policy.d.ts +1 -2
  155. package/dist/lib/named-backup-export.d.ts +0 -1
  156. package/dist/lib/oc-chatgpt-import-adapter.d.ts +0 -1
  157. package/dist/lib/oc-chatgpt-orchestrator.d.ts +0 -1
  158. package/dist/lib/oc-chatgpt-target-detection.d.ts +0 -1
  159. package/dist/lib/parallel-probe.d.ts +0 -1
  160. package/dist/lib/policy/runtime-policy-cache.d.ts +80 -0
  161. package/dist/lib/policy/runtime-policy-cache.js +310 -0
  162. package/dist/lib/policy/runtime-policy.d.ts +1 -2
  163. package/dist/lib/policy/runtime-policy.js +46 -19
  164. package/dist/lib/preemptive-quota-scheduler.d.ts +0 -1
  165. package/dist/lib/proactive-refresh.d.ts +0 -1
  166. package/dist/lib/prompts/codex-host-bridge.d.ts +0 -1
  167. package/dist/lib/prompts/codex.d.ts +0 -1
  168. package/dist/lib/prompts/fetch-utils.d.ts +0 -1
  169. package/dist/lib/prompts/host-codex-prompt.d.ts +0 -1
  170. package/dist/lib/quota-cache.d.ts +0 -1
  171. package/dist/lib/quota-cache.js +60 -36
  172. package/dist/lib/quota-probe.d.ts +0 -1
  173. package/dist/lib/quota-probe.js +5 -2
  174. package/dist/lib/quota-readiness.d.ts +0 -1
  175. package/dist/lib/recovery/constants.d.ts +0 -1
  176. package/dist/lib/recovery/storage.d.ts +0 -1
  177. package/dist/lib/recovery/types.d.ts +0 -1
  178. package/dist/lib/recovery.d.ts +0 -1
  179. package/dist/lib/redaction.d.ts +0 -1
  180. package/dist/lib/refresh-guardian.d.ts +0 -1
  181. package/dist/lib/refresh-lease.d.ts +0 -1
  182. package/dist/lib/refresh-queue.d.ts +0 -1
  183. package/dist/lib/request/client-cancellation.d.ts +0 -1
  184. package/dist/lib/request/error-classification.d.ts +0 -1
  185. package/dist/lib/request/failover-config.d.ts +0 -1
  186. package/dist/lib/request/failure-policy.d.ts +0 -1
  187. package/dist/lib/request/fetch-helpers.d.ts +0 -1
  188. package/dist/lib/request/headers.d.ts +0 -1
  189. package/dist/lib/request/helpers/input-utils.d.ts +0 -1
  190. package/dist/lib/request/helpers/input-utils.js +88 -34
  191. package/dist/lib/request/helpers/model-map.d.ts +0 -1
  192. package/dist/lib/request/helpers/tool-utils.d.ts +0 -1
  193. package/dist/lib/request/helpers/tool-utils.js +421 -4
  194. package/dist/lib/request/index.d.ts +0 -1
  195. package/dist/lib/request/rate-limit-backoff.d.ts +0 -1
  196. package/dist/lib/request/rate-limit-decision.d.ts +0 -1
  197. package/dist/lib/request/request-attempt-budget.d.ts +0 -1
  198. package/dist/lib/request/request-init.d.ts +0 -1
  199. package/dist/lib/request/request-resilience.d.ts +0 -1
  200. package/dist/lib/request/request-transformer.d.ts +0 -1
  201. package/dist/lib/request/request-transformer.js +50 -25
  202. package/dist/lib/request/response-compaction.d.ts +0 -1
  203. package/dist/lib/request/response-handler.d.ts +0 -1
  204. package/dist/lib/request/response-metadata.d.ts +0 -1
  205. package/dist/lib/request/response-outcome.d.ts +0 -1
  206. package/dist/lib/request/stream-failover-runtime.d.ts +0 -1
  207. package/dist/lib/request/stream-failover.d.ts +0 -1
  208. package/dist/lib/request/stream-failover.js +28 -4
  209. package/dist/lib/request/token-refresh.d.ts +0 -1
  210. package/dist/lib/request/url-rewriting.d.ts +0 -1
  211. package/dist/lib/request/wait-utils.d.ts +0 -1
  212. package/dist/lib/rotation.d.ts +0 -1
  213. package/dist/lib/routing-mutex.d.ts +0 -1
  214. package/dist/lib/routing-profiles.d.ts +16 -2
  215. package/dist/lib/routing-profiles.js +119 -44
  216. package/dist/lib/runtime/account-check-helpers.d.ts +0 -1
  217. package/dist/lib/runtime/account-check-types.d.ts +0 -1
  218. package/dist/lib/runtime/account-check.d.ts +0 -1
  219. package/dist/lib/runtime/account-manager-cache-entry.d.ts +0 -1
  220. package/dist/lib/runtime/account-manager-cache.d.ts +0 -1
  221. package/dist/lib/runtime/account-model-catalog.d.ts +1 -2
  222. package/dist/lib/runtime/account-pool.d.ts +0 -1
  223. package/dist/lib/runtime/account-reset-credits.d.ts +0 -1
  224. package/dist/lib/runtime/account-reset-credits.js +8 -2
  225. package/dist/lib/runtime/account-scope.d.ts +0 -1
  226. package/dist/lib/runtime/account-select-event.d.ts +0 -1
  227. package/dist/lib/runtime/account-selection.d.ts +0 -1
  228. package/dist/lib/runtime/account-state.d.ts +0 -1
  229. package/dist/lib/runtime/account-status.d.ts +0 -1
  230. package/dist/lib/runtime/account-storage-scope-entry.d.ts +0 -1
  231. package/dist/lib/runtime/api-model-capabilities.d.ts +0 -1
  232. package/dist/lib/runtime/api-model-runtime.d.ts +0 -1
  233. package/dist/lib/runtime/app-bind.d.ts +0 -1
  234. package/dist/lib/runtime/app-helper-selection.d.ts +0 -1
  235. package/dist/lib/runtime/auth-facade.d.ts +0 -1
  236. package/dist/lib/runtime/automatic-account-checks.d.ts +0 -1
  237. package/dist/lib/runtime/automatic-subscription-checks.d.ts +0 -1
  238. package/dist/lib/runtime/browser-oauth-flow.d.ts +0 -1
  239. package/dist/lib/runtime/capability-boost.d.ts +0 -1
  240. package/dist/lib/runtime/catalog-capabilities.d.ts +0 -1
  241. package/dist/lib/runtime/config-toml.d.ts +0 -1
  242. package/dist/lib/runtime/context-budget-settings.d.ts +0 -1
  243. package/dist/lib/runtime/event-handler.d.ts +0 -1
  244. package/dist/lib/runtime/first-run.d.ts +0 -1
  245. package/dist/lib/runtime/flagged-verify-types.d.ts +0 -1
  246. package/dist/lib/runtime/hydrate-emails.d.ts +0 -1
  247. package/dist/lib/runtime/inference-activity.d.ts +0 -1
  248. package/dist/lib/runtime/live-sync-entry.d.ts +0 -1
  249. package/dist/lib/runtime/live-sync.d.ts +0 -1
  250. package/dist/lib/runtime/loader-setup.d.ts +0 -1
  251. package/dist/lib/runtime/login-menu-accounts.d.ts +0 -1
  252. package/dist/lib/runtime/manual-oauth-flow.d.ts +0 -1
  253. package/dist/lib/runtime/model-discovery-status.d.ts +2 -3
  254. package/dist/lib/runtime/native-account-storage.d.ts +0 -1
  255. package/dist/lib/runtime/native-account-sync.d.ts +0 -1
  256. package/dist/lib/runtime/native-binding-lock.d.ts +0 -1
  257. package/dist/lib/runtime/native-client-auth.d.ts +0 -1
  258. package/dist/lib/runtime/native-provider-config.d.ts +0 -1
  259. package/dist/lib/runtime/native-rate-limits.d.ts +2 -1
  260. package/dist/lib/runtime/native-rate-limits.js +23 -6
  261. package/dist/lib/runtime/preemptive-quota.d.ts +0 -1
  262. package/dist/lib/runtime/quota-headers.d.ts +0 -1
  263. package/dist/lib/runtime/quota-probe.d.ts +0 -1
  264. package/dist/lib/runtime/quota-settings.d.ts +0 -1
  265. package/dist/lib/runtime/refresh-guardian-entry.d.ts +0 -1
  266. package/dist/lib/runtime/refresh-guardian.d.ts +0 -1
  267. package/dist/lib/runtime/request-init.d.ts +0 -1
  268. package/dist/lib/runtime/reset-credit-routing.d.ts +0 -1
  269. package/dist/lib/runtime/reset-credits.d.ts +0 -1
  270. package/dist/lib/runtime/response-output-history.d.ts +0 -1
  271. package/dist/lib/runtime/responses-websocket.d.ts +0 -1
  272. package/dist/lib/runtime/resume-picker.d.ts +0 -1
  273. package/dist/lib/runtime/rotation-account-selection.d.ts +0 -1
  274. package/dist/lib/runtime/rotation-proxy-state.d.ts +0 -1
  275. package/dist/lib/runtime/rotation-server-types.d.ts +0 -1
  276. package/dist/lib/runtime/rotation-storage-meta.d.ts +0 -1
  277. package/dist/lib/runtime/rotation-token-refresh.d.ts +0 -1
  278. package/dist/lib/runtime/runtime-capability-failures.d.ts +0 -1
  279. package/dist/lib/runtime/runtime-current-account.d.ts +0 -1
  280. package/dist/lib/runtime/runtime-observability.d.ts +0 -1
  281. package/dist/lib/runtime/runtime-services.d.ts +0 -1
  282. package/dist/lib/runtime/session-affinity-entry.d.ts +0 -1
  283. package/dist/lib/runtime/session-recovery.d.ts +0 -1
  284. package/dist/lib/runtime/status-marker.d.ts +0 -1
  285. package/dist/lib/runtime/storage-scope.d.ts +0 -1
  286. package/dist/lib/runtime/subscription-first-use.d.ts +0 -1
  287. package/dist/lib/runtime/subscription-quota-event.d.ts +0 -1
  288. package/dist/lib/runtime/subscription-quota-order.d.ts +0 -1
  289. package/dist/lib/runtime/toast.d.ts +0 -1
  290. package/dist/lib/runtime/ui-runtime-entry.d.ts +0 -1
  291. package/dist/lib/runtime/ui-runtime.d.ts +0 -1
  292. package/dist/lib/runtime/verify-flagged.d.ts +0 -1
  293. package/dist/lib/runtime/workspace-model-scopes.d.ts +0 -1
  294. package/dist/lib/runtime-constants.d.ts +0 -1
  295. package/dist/lib/runtime-paths.d.ts +0 -1
  296. package/dist/lib/runtime-rotation-proxy.d.ts +1 -1
  297. package/dist/lib/runtime-rotation-proxy.js +133 -0
  298. package/dist/lib/schemas.d.ts +35 -13
  299. package/dist/lib/schemas.js +19 -2
  300. package/dist/lib/session-affinity.d.ts +0 -1
  301. package/dist/lib/shutdown.d.ts +0 -1
  302. package/dist/lib/storage/account-clear-entry.d.ts +0 -1
  303. package/dist/lib/storage/account-clear.d.ts +0 -1
  304. package/dist/lib/storage/account-match-utils.d.ts +0 -1
  305. package/dist/lib/storage/account-persistence.d.ts +0 -1
  306. package/dist/lib/storage/account-port.d.ts +0 -1
  307. package/dist/lib/storage/account-save-entry.d.ts +0 -1
  308. package/dist/lib/storage/account-save.d.ts +9 -2
  309. package/dist/lib/storage/account-save.js +4 -1
  310. package/dist/lib/storage/account-snapshot.d.ts +0 -1
  311. package/dist/lib/storage/backup-metadata-builder.d.ts +0 -1
  312. package/dist/lib/storage/backup-metadata.d.ts +0 -1
  313. package/dist/lib/storage/backup-paths.d.ts +0 -1
  314. package/dist/lib/storage/backup-restore.d.ts +0 -1
  315. package/dist/lib/storage/cache-artifacts.d.ts +0 -1
  316. package/dist/lib/storage/credential-sidecars.d.ts +0 -1
  317. package/dist/lib/storage/error-hints.d.ts +0 -1
  318. package/dist/lib/storage/file-lock.d.ts +0 -1
  319. package/dist/lib/storage/file-lock.js +33 -1
  320. package/dist/lib/storage/file-paths.d.ts +0 -1
  321. package/dist/lib/storage/fixture-guards.d.ts +0 -1
  322. package/dist/lib/storage/flagged-entry.d.ts +0 -1
  323. package/dist/lib/storage/flagged-load-entry.d.ts +0 -1
  324. package/dist/lib/storage/flagged-save-entry.d.ts +0 -1
  325. package/dist/lib/storage/flagged-storage-file.d.ts +0 -1
  326. package/dist/lib/storage/flagged-storage-io.d.ts +0 -1
  327. package/dist/lib/storage/flagged-storage.d.ts +0 -1
  328. package/dist/lib/storage/gitignore.d.ts +0 -1
  329. package/dist/lib/storage/hash.d.ts +0 -1
  330. package/dist/lib/storage/health.d.ts +0 -1
  331. package/dist/lib/storage/identity.d.ts +0 -1
  332. package/dist/lib/storage/import-export.d.ts +0 -1
  333. package/dist/lib/storage/json-store-lock.d.ts +105 -0
  334. package/dist/lib/storage/json-store-lock.js +259 -0
  335. package/dist/lib/storage/metadata-section.d.ts +0 -1
  336. package/dist/lib/storage/migrations.d.ts +0 -1
  337. package/dist/lib/storage/migrations.js +31 -18
  338. package/dist/lib/storage/named-backup-entry.d.ts +0 -1
  339. package/dist/lib/storage/named-backups-entry.d.ts +0 -1
  340. package/dist/lib/storage/named-backups.d.ts +0 -1
  341. package/dist/lib/storage/path-state.d.ts +0 -1
  342. package/dist/lib/storage/paths.d.ts +0 -1
  343. package/dist/lib/storage/pending-auth.d.ts +9 -3
  344. package/dist/lib/storage/pending-auth.js +30 -8
  345. package/dist/lib/storage/project-migration.d.ts +0 -1
  346. package/dist/lib/storage/public-types.d.ts +0 -1
  347. package/dist/lib/storage/record-identity.d.ts +0 -1
  348. package/dist/lib/storage/record-utils.d.ts +0 -1
  349. package/dist/lib/storage/restore-assessment.d.ts +0 -1
  350. package/dist/lib/storage/restore-backup-entry.d.ts +0 -1
  351. package/dist/lib/storage/restore-metadata.d.ts +0 -1
  352. package/dist/lib/storage/save-retry.d.ts +0 -1
  353. package/dist/lib/storage/snapshot-inspectors.d.ts +0 -1
  354. package/dist/lib/storage/snapshot-merge.d.ts +0 -1
  355. package/dist/lib/storage/storage-parser.d.ts +2 -1
  356. package/dist/lib/storage/storage-parser.js +10 -2
  357. package/dist/lib/storage/transactions.d.ts +0 -1
  358. package/dist/lib/storage.d.ts +15 -2
  359. package/dist/lib/storage.js +612 -25
  360. package/dist/lib/synthetic-response.d.ts +0 -1
  361. package/dist/lib/table-formatter.d.ts +0 -1
  362. package/dist/lib/temp-path.d.ts +0 -1
  363. package/dist/lib/tools/hashline-tools.d.ts +0 -1
  364. package/dist/lib/types.d.ts +0 -1
  365. package/dist/lib/ui/ansi.d.ts +0 -1
  366. package/dist/lib/ui/auth-menu-builder.d.ts +0 -1
  367. package/dist/lib/ui/auth-menu.d.ts +0 -1
  368. package/dist/lib/ui/check-progress.d.ts +0 -1
  369. package/dist/lib/ui/confirm.d.ts +0 -1
  370. package/dist/lib/ui/display-width.d.ts +0 -1
  371. package/dist/lib/ui/format.d.ts +0 -1
  372. package/dist/lib/ui/index.d.ts +0 -1
  373. package/dist/lib/ui/runtime.d.ts +0 -1
  374. package/dist/lib/ui/select.d.ts +0 -1
  375. package/dist/lib/ui/theme.d.ts +0 -1
  376. package/dist/lib/ui/ui-copy.d.ts +0 -1
  377. package/dist/lib/unified-settings.d.ts +13 -11
  378. package/dist/lib/unified-settings.js +22 -14
  379. package/dist/lib/update-notice.d.ts +0 -1
  380. package/dist/lib/usage/index.d.ts +0 -1
  381. package/dist/lib/usage/ledger.d.ts +0 -1
  382. package/dist/lib/usage/pricing.d.ts +0 -1
  383. package/dist/lib/usage/redaction.d.ts +0 -1
  384. package/dist/lib/usage/stream-usage-deferral.d.ts +0 -1
  385. package/dist/lib/usage/types.d.ts +0 -1
  386. package/dist/lib/usage/usage-extraction.d.ts +0 -1
  387. package/dist/lib/utils.d.ts +21 -1
  388. package/dist/lib/utils.js +31 -0
  389. package/dist/lib/wsl.d.ts +0 -1
  390. package/package.json +11 -5
  391. package/scripts/codex-bin-resolver.js +20 -1
  392. package/scripts/codex-multi-auth.js +7 -1
  393. package/scripts/codex.js +288 -7
  394. package/skills/codex-auth-setup/SKILL.md +73 -0
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Hot-path caches for the runtime policy stores.
3
+ *
4
+ * `loadRuntimePolicyState` runs once per proxied request, and the proxy is a
5
+ * long-running process while the stores are mutated by separate CLI processes
6
+ * (`account pause`, `budget`, `routing profile`). The naive path re-read +
7
+ * re-parsed three JSON files and re-walked the project-root ancestor chain on
8
+ * every request. These caches keep semantics identical while reducing the
9
+ * steady-state cost to a handful of `statSync` calls:
10
+ *
11
+ * - Store entries are keyed by absolute file path and guarded by an
12
+ * `mtimeMs:ctimeMs:size` fingerprint (same approach as
13
+ * `lib/runtime/rotation-storage-meta.ts`): the store is only re-read when
14
+ * the fingerprint changes. `ctimeMs` is part of the fingerprint because it
15
+ * cannot be pinned back the way `mtimeMs` can — on POSIX every write bumps
16
+ * it, and on Windows every rename-replacement produces a new file creation
17
+ * time — so a rewrite that preserves mtime+size is still caught instead of
18
+ * being served stale for the TTL. A fingerprint match is additionally only
19
+ * trusted once the file's own mtime has settled past `MTIME_SETTLE_MS` —
20
+ * two rapid writes can share a coarse mtime tick on some filesystems (FAT
21
+ * 2s, ~1s Windows/network volumes), so inside the settle window we always
22
+ * re-read. `STORE_CACHE_TTL_MS` bounds staleness for the pathological case
23
+ * where a rewrite leaves mtime, ctime and size unchanged, and
24
+ * `STORE_CACHE_MAX_ENTRIES` bounds the map (tests rotate through many
25
+ * per-test `CODEX_MULTI_AUTH_DIR` temp dirs).
26
+ *
27
+ * Two failure modes get dedicated handling so they cannot poison the cache:
28
+ * a file rewritten *while* the loader is reading it is detected by
29
+ * re-statting after the load and only caching when the fingerprint is
30
+ * stable across the read (an old parse is never stored under the new
31
+ * file's fingerprint); and a read that resolves to the empty store while
32
+ * bytes still exist on disk — exhausted EBUSY/EPERM retries or corrupt
33
+ * JSON both surface as `emptyStore()` from the loaders — is served but
34
+ * never cached, so a transient failure cannot hide a real policy for the
35
+ * full TTL.
36
+ *
37
+ * - Project resolution (`findProjectRoot` + `resolveProjectStorageIdentityRoot`
38
+ * + `getProjectStorageKey`) is cached per resolved startDir for
39
+ * `PROJECT_RESOLUTION_TTL_MS` and guarded by two fingerprints: an
40
+ * mtime/ctime/size fingerprint of the resolved root's `.git` entry — the file
41
+ * whose contents drive worktree identity resolution — and the joined
42
+ * fingerprints of every ancestor directory that could host a marker, whose
43
+ * mtimes change when a marker file is created or deleted. Entries
44
+ * therefore invalidate when the project context differs (different
45
+ * startDir key, different resolved root), when the `.git` entry changes,
46
+ * or when a marker appears/disappears in a watched ancestor — the TTL is
47
+ * only the residual bound for coarse-mtime filesystems that report no
48
+ * change at all.
49
+ *
50
+ * Values held in the caches are shared, mutable objects. Callers must treat
51
+ * returned values as read-only; `loadRuntimePolicyState` clones what it hands
52
+ * out.
53
+ */
54
+ import { existsSync, statSync } from "node:fs";
55
+ import { dirname, join, resolve } from "node:path";
56
+ import { getAccountPolicyPath, loadAccountPolicyStore, } from "../account-policy.js";
57
+ import { getBudgetGuardPath, loadBudgetGuardStore, } from "../budget-guard.js";
58
+ import { getRoutingProfilesPath, loadRoutingProfileStore, } from "../routing-profiles.js";
59
+ import { findProjectRoot, getProjectStorageKey, resolveProjectStorageIdentityRoot, } from "../storage/paths.js";
60
+ // Mirrors MTIME_SHORTCIRCUIT_SETTLE_MS in rotation-storage-meta.ts: 2s
61
+ // comfortably exceeds the coarsest mtime granularity we expect in practice.
62
+ const MTIME_SETTLE_MS = 2_000;
63
+ const STORE_CACHE_TTL_MS = 30_000;
64
+ const STORE_CACHE_MAX_ENTRIES = 32;
65
+ // Retries while the file's fingerprint keeps moving across a load (a writer
66
+ // racing us). Beyond this bound the freshest value is returned uncached.
67
+ const STORE_LOAD_MAX_ATTEMPTS = 3;
68
+ const PROJECT_RESOLUTION_TTL_MS = 5_000;
69
+ const PROJECT_RESOLUTION_MAX_ENTRIES = 64;
70
+ const PROJECT_WATCH_MAX_ANCESTORS = 48;
71
+ const ACCOUNT_POLICY_CACHE = new Map();
72
+ const BUDGET_GUARD_CACHE = new Map();
73
+ const ROUTING_PROFILES_CACHE = new Map();
74
+ const PROJECT_RESOLUTION_CACHE = new Map();
75
+ let statErrorSeq = 0;
76
+ function statFile(path) {
77
+ try {
78
+ const stats = statSync(path);
79
+ return {
80
+ stat: {
81
+ mtimeMs: stats.mtimeMs,
82
+ ctimeMs: stats.ctimeMs,
83
+ size: stats.size,
84
+ },
85
+ fingerprint: `${stats.mtimeMs}:${stats.ctimeMs}:${stats.size}`,
86
+ };
87
+ }
88
+ catch (error) {
89
+ const code = error?.code;
90
+ if (code === "ENOENT" || code === "ENOTDIR") {
91
+ return { stat: null, fingerprint: null };
92
+ }
93
+ statErrorSeq += 1;
94
+ return { stat: null, fingerprint: `stat-error:${statErrorSeq}` };
95
+ }
96
+ }
97
+ function evictOldest(map, maxEntries) {
98
+ let excess = map.size - maxEntries;
99
+ for (const key of map.keys()) {
100
+ if (excess <= 0)
101
+ return;
102
+ map.delete(key);
103
+ excess -= 1;
104
+ }
105
+ }
106
+ /**
107
+ * mtime+ctime+size-fingerprinted wrapper around an existing store loader. On
108
+ * a fingerprint hit the cached store is returned WITHOUT re-reading or
109
+ * re-parsing the file; on a miss the real loader runs (preserving its retry,
110
+ * normalization, and warn-on-corrupt semantics) and the entry is keyed on a
111
+ * fingerprint that must be stable ACROSS the load — re-statting after the
112
+ * read catches a concurrent rewrite that would otherwise pin the old parse
113
+ * under the new file's fingerprint once the new mtime settles. A load that
114
+ * returns the empty store while the file still exists is a failed or
115
+ * undecodable read, never a cacheable "empty file" answer. The returned
116
+ * object is the cached instance — callers must not mutate it.
117
+ */
118
+ async function loadStoreCached(input) {
119
+ const now = Date.now();
120
+ const sample = statFile(input.path);
121
+ const cached = input.cache.get(input.path);
122
+ if (cached &&
123
+ cached.fingerprint === sample.fingerprint &&
124
+ now - cached.loadedAt < STORE_CACHE_TTL_MS &&
125
+ (sample.stat === null || now - sample.stat.mtimeMs > MTIME_SETTLE_MS)) {
126
+ // Refresh recency for LRU eviction.
127
+ input.cache.delete(input.path);
128
+ input.cache.set(input.path, cached);
129
+ return cached.value;
130
+ }
131
+ for (let attempt = 0; attempt < STORE_LOAD_MAX_ATTEMPTS; attempt += 1) {
132
+ const before = statFile(input.path).fingerprint;
133
+ const value = await input.load();
134
+ const after = statFile(input.path);
135
+ if (before !== after.fingerprint) {
136
+ // The file moved under the read: `value` may predate the bytes the
137
+ // new fingerprint describes. Retry while the writer is still
138
+ // racing; on the last attempt the value is still a real read, it
139
+ // is simply not safe to cache.
140
+ if (attempt + 1 === STORE_LOAD_MAX_ATTEMPTS)
141
+ return value;
142
+ continue;
143
+ }
144
+ if (after.stat !== null && input.isEmptyStore(value)) {
145
+ // The loaders resolve exhausted EBUSY/EPERM reads and corrupt JSON
146
+ // to the empty store — indistinguishable from a legitimately empty
147
+ // file except that bytes still exist on disk. Serve it, but do
148
+ // not cache: the next request retries the read instead of hiding
149
+ // an existing policy for the full TTL.
150
+ return value;
151
+ }
152
+ input.cache.set(input.path, {
153
+ fingerprint: after.fingerprint,
154
+ loadedAt: Date.now(),
155
+ value,
156
+ });
157
+ evictOldest(input.cache, STORE_CACHE_MAX_ENTRIES);
158
+ return value;
159
+ }
160
+ // Unreachable: every iteration returns or continues past the bound.
161
+ throw new Error("runtime policy cache load attempts exhausted");
162
+ }
163
+ /**
164
+ * Cached variant of `loadAccountPolicyStore`. The returned store is shared
165
+ * cache state — treat as read-only.
166
+ */
167
+ export function loadAccountPolicyStoreCached() {
168
+ return loadStoreCached({
169
+ path: getAccountPolicyPath(),
170
+ cache: ACCOUNT_POLICY_CACHE,
171
+ load: loadAccountPolicyStore,
172
+ isEmptyStore: (store) => Object.keys(store.accounts).length === 0,
173
+ });
174
+ }
175
+ /**
176
+ * Cached variant of `loadBudgetGuardStore`. The returned store is shared
177
+ * cache state — treat as read-only.
178
+ */
179
+ export function loadBudgetGuardStoreCached() {
180
+ return loadStoreCached({
181
+ path: getBudgetGuardPath(),
182
+ cache: BUDGET_GUARD_CACHE,
183
+ load: loadBudgetGuardStore,
184
+ isEmptyStore: (store) => Object.keys(store.limits).length === 0,
185
+ });
186
+ }
187
+ function loadRoutingProfileStoreCached() {
188
+ return loadStoreCached({
189
+ path: getRoutingProfilesPath(),
190
+ cache: ROUTING_PROFILES_CACHE,
191
+ load: loadRoutingProfileStore,
192
+ isEmptyStore: (store) => Object.keys(store.profiles).length === 0,
193
+ });
194
+ }
195
+ function gitEntryFingerprint(projectRoot) {
196
+ return statFile(join(projectRoot, ".git")).fingerprint;
197
+ }
198
+ function watchDirsFingerprint(dirs) {
199
+ return dirs.map((dir) => statFile(dir).fingerprint).join("|");
200
+ }
201
+ /**
202
+ * Ancestor directories whose member changes can alter `findProjectRoot`'s
203
+ * answer for `startDir`: every level between startDir and the resolved root
204
+ * (inclusive) — a marker created below the current root outranks it — and,
205
+ * when the root was not found via `.git` (or no root exists at all), every
206
+ * level up to the filesystem root, because a `.git` appearing at any higher
207
+ * ancestor would take precedence over the current marker/rootless result.
208
+ * Directory mtimes change when entries are created or deleted, so new or
209
+ * removed markers invalidate the cached resolution on the next request.
210
+ */
211
+ function ancestorWatchDirs(startDir, projectRoot) {
212
+ const stopAt = projectRoot !== null && existsSync(join(projectRoot, ".git"))
213
+ ? projectRoot
214
+ : null;
215
+ const dirs = [];
216
+ let current = resolve(startDir);
217
+ for (let depth = 0; depth < PROJECT_WATCH_MAX_ANCESTORS; depth += 1) {
218
+ dirs.push(current);
219
+ if (current === stopAt)
220
+ break;
221
+ const parent = dirname(current);
222
+ if (parent === current)
223
+ break;
224
+ current = parent;
225
+ }
226
+ return dirs;
227
+ }
228
+ function resolveProject(startDir) {
229
+ const projectRoot = findProjectRoot(startDir);
230
+ const identityRoot = projectRoot
231
+ ? resolveProjectStorageIdentityRoot(projectRoot)
232
+ : null;
233
+ const watchDirs = ancestorWatchDirs(startDir, projectRoot);
234
+ return {
235
+ projectRoot,
236
+ identityRoot,
237
+ projectKey: identityRoot ? getProjectStorageKey(identityRoot) : null,
238
+ gitFingerprint: projectRoot ? gitEntryFingerprint(projectRoot) : null,
239
+ watchDirs,
240
+ watchFingerprint: watchDirsFingerprint(watchDirs),
241
+ resolvedAt: Date.now(),
242
+ };
243
+ }
244
+ /**
245
+ * `findProjectRoot` + `resolveProjectStorageIdentityRoot` cached per resolved
246
+ * startDir. Hits require a fresh TTL, an unchanged `.git` fingerprint on the
247
+ * resolved root (editing the worktree pointer or re-creating `.git`
248
+ * re-resolves immediately), and unchanged ancestor-directory fingerprints
249
+ * (marker files created or deleted anywhere along the watched chain bump the
250
+ * parent dir's mtime and re-resolve immediately). The TTL remains the bound
251
+ * for filesystems too coarse to report any of those changes.
252
+ */
253
+ function resolveProjectCached(startDir) {
254
+ const now = Date.now();
255
+ const cacheKey = resolve(startDir);
256
+ const cached = PROJECT_RESOLUTION_CACHE.get(cacheKey);
257
+ if (cached &&
258
+ now - cached.resolvedAt < PROJECT_RESOLUTION_TTL_MS &&
259
+ (!cached.projectRoot ||
260
+ gitEntryFingerprint(cached.projectRoot) === cached.gitFingerprint) &&
261
+ watchDirsFingerprint(cached.watchDirs) === cached.watchFingerprint) {
262
+ PROJECT_RESOLUTION_CACHE.delete(cacheKey);
263
+ PROJECT_RESOLUTION_CACHE.set(cacheKey, cached);
264
+ return cached;
265
+ }
266
+ const resolution = resolveProject(cacheKey);
267
+ PROJECT_RESOLUTION_CACHE.set(cacheKey, resolution);
268
+ evictOldest(PROJECT_RESOLUTION_CACHE, PROJECT_RESOLUTION_MAX_ENTRIES);
269
+ return resolution;
270
+ }
271
+ /**
272
+ * Cached variant of `resolveProjectRoutingProfile` for the per-request path.
273
+ * The project context (root/identity/key) comes from the bounded resolution
274
+ * cache; the profile itself is re-derived on every call from the
275
+ * fingerprint-guarded routing-profiles store, so profile edits are picked up
276
+ * as soon as the file's mtime/size changes and distinct project contexts can
277
+ * never observe each other's entries.
278
+ */
279
+ export async function resolveProjectRoutingProfileCached(startDir = process.cwd()) {
280
+ const resolution = resolveProjectCached(startDir);
281
+ if (!resolution.projectRoot ||
282
+ !resolution.identityRoot ||
283
+ !resolution.projectKey) {
284
+ return {
285
+ startDir,
286
+ projectRoot: null,
287
+ identityRoot: null,
288
+ projectKey: null,
289
+ profile: null,
290
+ };
291
+ }
292
+ const store = await loadRoutingProfileStoreCached();
293
+ return {
294
+ startDir,
295
+ projectRoot: resolution.projectRoot,
296
+ identityRoot: resolution.identityRoot,
297
+ projectKey: resolution.projectKey,
298
+ profile: store.profiles[resolution.projectKey] ?? null,
299
+ };
300
+ }
301
+ /**
302
+ * Test-only: drop every cached store value and project resolution so each
303
+ * test starts from a clean read-from-disk state.
304
+ */
305
+ export function resetRuntimePolicyCacheForTests() {
306
+ ACCOUNT_POLICY_CACHE.clear();
307
+ BUDGET_GUARD_CACHE.clear();
308
+ ROUTING_PROFILES_CACHE.clear();
309
+ PROJECT_RESOLUTION_CACHE.clear();
310
+ }
@@ -1,7 +1,7 @@
1
1
  import type { CapabilityPolicyStore } from "../capability-policy.js";
2
2
  import { type AccountPolicyStore } from "../account-policy.js";
3
3
  import { type BudgetGuardEvaluation, type BudgetGuardStore } from "../budget-guard.js";
4
- import { type ProjectRoutingProfileContext } from "../routing-profiles.js";
4
+ import type { ProjectRoutingProfileContext } from "../routing-profiles.js";
5
5
  import { appendUsageLedgerRow, type UsageLedgerOperation, type UsageLedgerOutcome, type UsageLedgerSource, type UsageServiceTier } from "../usage/index.js";
6
6
  export interface RuntimePolicyAccount {
7
7
  index: number;
@@ -73,4 +73,3 @@ export declare function createRuntimeUsageRecorder(input: {
73
73
  startedAt?: number;
74
74
  append?: typeof appendUsageLedgerRow;
75
75
  }): RuntimeUsageRecorder;
76
- //# sourceMappingURL=runtime-policy.d.ts.map
@@ -1,15 +1,25 @@
1
1
  import { resolveEntitlementAccountKey } from "../entitlement-cache.js";
2
- import { getAccountPolicyKey, loadAccountPolicyStore, } from "../account-policy.js";
3
- import { evaluateBudgetGuard, getBudgetWindowStart, loadBudgetGuardStore, normalizeBudgetKey, } from "../budget-guard.js";
4
- import { resolveProjectRoutingProfile, } from "../routing-profiles.js";
5
- import { appendUsageLedgerRow, summarizeUsageLedger, } from "../usage/index.js";
2
+ import { getAccountPolicyKey, } from "../account-policy.js";
3
+ import { evaluateBudgetGuard, getBudgetWindowStart, normalizeBudgetKey, } from "../budget-guard.js";
4
+ import { loadAccountPolicyStoreCached, loadBudgetGuardStoreCached, resolveProjectRoutingProfileCached, } from "./runtime-policy-cache.js";
5
+ import { appendUsageLedgerRow, readUsageLedgerRows, summarizeUsageRows, } from "../usage/index.js";
6
6
  export async function loadRuntimePolicyState(startDir = process.cwd()) {
7
+ // Cached loaders: each proxied request costs a few statSync calls instead of
8
+ // three file reads + JSON parses + the project-root ancestor walk. Stores
9
+ // re-read whenever their file's mtime/size changes (or within the mtime
10
+ // settle window, or past the cache TTL), and the project context
11
+ // re-resolves when the startDir context or `.git` entry changes — so
12
+ // CLI-side writes are honored without per-request reparsing. See
13
+ // lib/policy/runtime-policy-cache.ts.
7
14
  const [accountPolicies, budgets, project] = await Promise.all([
8
- loadAccountPolicyStore(),
9
- loadBudgetGuardStore(),
10
- resolveProjectRoutingProfile(startDir),
15
+ loadAccountPolicyStoreCached(),
16
+ loadBudgetGuardStoreCached(),
17
+ resolveProjectRoutingProfileCached(startDir),
11
18
  ]);
12
- return { accountPolicies, budgets, project };
19
+ // The caches share store objects across calls. Every caller previously
20
+ // received a private parse, so hand out a private clone: a caller mutating
21
+ // its state must not poison the cache for the next request.
22
+ return structuredClone({ accountPolicies, budgets, project });
13
23
  }
14
24
  function normalizeToken(value) {
15
25
  const trimmed = value?.trim().toLowerCase();
@@ -49,19 +59,36 @@ async function evaluateBudgets(input) {
49
59
  if (budgetKey)
50
60
  keys.add(budgetKey);
51
61
  }
52
- const evaluations = [];
62
+ const matchingLimits = [];
53
63
  for (const key of keys) {
54
64
  const limit = input.state.budgets.limits[key];
55
- if (!limit)
56
- continue;
57
- const summary = await summarizeUsageLedger({
58
- since: getBudgetWindowStart(limit.window, input.now),
59
- until: input.now,
60
- // Budget windows (e.g. monthly) can span a ledger rotation. Without archives,
61
- // rotated-out rows are dropped from the sum, under-counting spend and letting
62
- // usage exceed the limit within the active window (quota-forecast-03).
63
- includeArchives: true,
64
- });
65
+ if (limit)
66
+ matchingLimits.push(limit);
67
+ }
68
+ if (matchingLimits.length === 0)
69
+ return [];
70
+ // Read + parse the ledger ONCE per evaluation, not once per matching key —
71
+ // each summarizeUsageLedger call used to reparse every usage-ledger*.jsonl
72
+ // line, making the request path O(ledger rows x budget keys). Budget
73
+ // windows (e.g. monthly) can span a ledger rotation, so archives are
74
+ // included; without them rotated-out rows are dropped from the sum,
75
+ // under-counting spend and letting usage exceed the limit within the
76
+ // active window (quota-forecast-03). Distinct windows still need distinct
77
+ // `since` filters, so rows are summarized per window in memory — the same
78
+ // summarizeUsageRows that summarizeUsageLedger applied to the same rows
79
+ // and query produces an identical UsageSummary.
80
+ const rows = await readUsageLedgerRows({ includeArchives: true });
81
+ const summariesByWindow = new Map();
82
+ const evaluations = [];
83
+ for (const limit of matchingLimits) {
84
+ let summary = summariesByWindow.get(limit.window);
85
+ if (!summary) {
86
+ summary = summarizeUsageRows(rows, {
87
+ since: getBudgetWindowStart(limit.window, input.now),
88
+ until: input.now,
89
+ });
90
+ summariesByWindow.set(limit.window, summary);
91
+ }
65
92
  evaluations.push(evaluateBudgetGuard(limit, summary));
66
93
  }
67
94
  return evaluations;
@@ -64,4 +64,3 @@ export declare class PreemptiveQuotaScheduler {
64
64
  clearAll(): void;
65
65
  prune(now?: number): number;
66
66
  }
67
- //# sourceMappingURL=preemptive-quota-scheduler.d.ts.map
@@ -63,4 +63,3 @@ export declare function refreshExpiringAccounts(accounts: ManagedAccount[], buff
63
63
  export declare function applyRefreshResult(account: ManagedAccount, result: Extract<TokenResult, {
64
64
  type: "success";
65
65
  }>): void;
66
- //# sourceMappingURL=proactive-refresh.d.ts.map
@@ -8,4 +8,3 @@
8
8
  * Token Count: ~450 tokens (~90% reduction vs full host prompt)
9
9
  */
10
10
  export declare const CODEX_HOST_BRIDGE = "# Codex Host Bridge\n\nYou are running Codex through a host terminal coding runtime. The host provides specific tools to help you work efficiently.\n\n## CRITICAL: Tool Usage\n\n<critical_rule priority=\"0\">\napply_patch/applyPatch are Codex names, but host tool names vary by version.\n- Inspect the actual tool list before editing.\n- If `edit` exists: use `edit` for precise in-place string replacements and hashline edits.\n- If `edit` is absent and `apply_patch` exists: use `apply_patch` for those precise/hashline edits in this plugin.\n- For diff-style or multi-line structural edits: use `patch` if available, otherwise use `apply_patch`.\n- In this plugin, `edit` / `apply_patch` also support hashline refs (`lineRef`, `endLineRef`, `operation`, `content`)\n</critical_rule>\n\n<critical_rule priority=\"0\">\nUPDATE_PLAN DOES NOT EXIST -> USE \"todowrite\" INSTEAD\n- NEVER use: update_plan, updatePlan, read_plan, readPlan\n- ALWAYS use: todowrite for task/plan updates, todoread to read plans\n- Before plan operations: Verify you're using \"todowrite\", NOT \"update_plan\"\n</critical_rule>\n\n## Available Host Tools\n\n**File Operations:**\n- `write` - Create new files\n - Overwriting existing files requires a prior Read in this session; default to ASCII unless the file already uses Unicode.\n- `edit` - Modify existing files with string replacement (version-dependent)\n - Requires a prior Read in this session; preserve exact indentation; ensure `oldString` uniquely matches or use `replaceAll`; edit fails if ambiguous or missing.\n - Never pass unresolved template placeholders in `oldString` (e.g. `${TARGET_SNIPPET}`); `oldString` must be literal text copied from the current file.\n - For complex multi-line changes: break into multiple sequential edit calls, each with unique oldString context.\n- `apply_patch` - May be the edit/patch tool name in newer host builds (version-dependent)\n - In this plugin, `apply_patch` also accepts hashline edit args (`path`, `lineRef`, `endLineRef`, `operation`, `content`) when `edit` is not available.\n- `hashline_read` - Read file with hashline refs (`L<line>#<hash>`) for deterministic edits\n- `patch` - Apply diff-style patches for multi-line updates (version-dependent)\n- `read` - Read file contents\n\nNote: Tool naming is version-dependent. If `edit` is unavailable but `apply_patch` exists, use `apply_patch`.\n\nWhen available, prefer hashline workflow for reliability:\n1. Call `hashline_read` for target file and capture refs.\n2. Call `edit` or `apply_patch` with `lineRef` (and optional `endLineRef`) plus `operation` and `content`.\n3. Use legacy `oldString` / `newString` only when hashline refs are unavailable, and only with literal file text (no template placeholders).\n\n**Search/Discovery:**\n- `grep` - Search file contents (tool, not bash grep); use `include` to filter patterns; set `path` only when not searching workspace root; for cross-file match counts use bash with `rg`.\n- `glob` - Find files by pattern; defaults to workspace cwd unless `path` is set.\n- `list` - List directories (requires absolute paths)\n\n**Execution:**\n- `bash` - Run shell commands\n - No workdir parameter; do not include it in tool calls.\n - Always include a short description for the command.\n - Do not use cd; use absolute paths in commands.\n - Quote paths containing spaces with double quotes.\n - Chain multiple commands with ';' or '&&'; avoid newlines.\n - Use Grep/Glob tools for searches; only use bash with `rg` when you need counts or advanced features.\n - Do not use `ls`/`cat` in bash; use `list`/`read` tools instead.\n - For deletions (rm), verify by listing parent dir with `list`.\n\n**Network:**\n- `webfetch` - Fetch web content\n - Use fully-formed URLs (http/https; http auto-upgrades to https).\n - Always set `format` to one of: text | markdown | html; prefer markdown unless otherwise required.\n - Read-only; short cache window.\n\n**Task Management:**\n- `todowrite` - Manage tasks/plans (REPLACES update_plan)\n- `todoread` - Read current plan\n\n## Substitution Rules\n\nBase instruction says: You MUST use instead:\napply_patch -> patch (preferred if available), otherwise edit/apply_patch based on actual tool list\nupdate_plan -> todowrite\nread_plan -> todoread\n\n**Path Usage:** Use per-tool conventions to avoid conflicts:\n- Tool calls: `read`, `edit`, `write`, `list` require absolute paths.\n- Searches: `grep`/`glob` default to the workspace cwd; prefer relative include patterns; set `path` only when a different root is needed.\n- Presentation: In assistant messages, show workspace-relative paths; use absolute paths only inside tool calls.\n- Tool schema overrides general path preferences-do not convert required absolute paths to relative.\n\n## Verification Checklist\n\nBefore file/plan modifications:\n1. Am I using the actual available edit tool name (`edit`, `patch`, or `apply_patch`)?\n2. Am I using \"todowrite\" NOT \"update_plan\"?\n3. Is this tool in the approved list above?\n4. Am I following each tool's path requirements?\n\nIf ANY answer is NO -> STOP and correct before proceeding.\n\n## Host Working Style\n\n**Communication:**\n- Send brief preambles (8-12 words) before tool calls, building on prior context\n- Provide progress updates during longer tasks\n\n**Execution:**\n- Keep working autonomously until query is fully resolved before yielding\n- Don't return to user with partial solutions\n\n**Code Approach:**\n- New projects: Be ambitious and creative\n- Existing codebases: Surgical precision - modify only what's requested unless explicitly instructed to do otherwise\n\n**Testing:**\n- If tests exist: Start specific to your changes, then broader validation\n\n## Advanced Tools\n\n**Task Tool (Sub-Agents):**\n- Use the Task tool (functions.task) to launch sub-agents\n- Check the Task tool description for current agent types and their capabilities\n- Useful for complex analysis, specialized workflows, or tasks requiring isolated context\n- The agent list is dynamically generated - refer to tool schema for available agents\n\n**Parallelization:**\n- When multiple independent tool calls are needed, use multi_tool_use.parallel to run them concurrently.\n- Reserve sequential calls for ordered or data-dependent steps.\n\n**MCP Tools:**\n- Model Context Protocol servers provide additional capabilities\n- MCP tools are prefixed: `mcp__<server-name>__<tool-name>`\n- Check your available tools for MCP integrations\n- Use when the tool's functionality matches your task needs\n\n## What Remains from Codex\n \nSandbox policies, approval mechanisms, final answer formatting, git commit protocols, and file reference formats all follow Codex instructions. In approval policy \"never\", never request escalations.\n\n## Approvals & Safety\n- Assume workspace-write filesystem, network enabled, approval on-failure unless explicitly stated otherwise.\n- When a command fails due to sandboxing or permissions, retry with escalated permissions if allowed by policy, including a one-line justification.\n- Treat destructive commands (e.g., `rm`, `git reset --hard`) as requiring explicit user request or approval.\n- Never run `git reset --hard`, `git checkout --`, or force deletes unless the user explicitly asked for that exact action.\n- `request_user_input` is Plan-mode only; do not call it in Default mode.\n- When uncertain, prefer non-destructive verification first (e.g., confirm file existence with `list`, then delete with `bash`).";
11
- //# sourceMappingURL=codex-host-bridge.d.ts.map
@@ -35,4 +35,3 @@ export declare function prewarmCodexInstructions(models?: string[]): void;
35
35
  * Tool remapping instructions for host runtime tools
36
36
  */
37
37
  export declare const TOOL_REMAP_MESSAGE = "<user_instructions priority=\"0\">\n<environment_override priority=\"0\">\nYOU ARE IN A DIFFERENT ENVIRONMENT. These instructions override ALL previous tool references.\n</environment_override>\n\n<tool_replacements priority=\"0\">\n<critical_rule priority=\"0\">\napply_patch/applyPatch are Codex names, but host tool names vary by version.\n- Inspect the actual tool list before editing.\n- If edit exists: use edit for precise in-place string replacements and hashline edits.\n- If edit is absent and apply_patch exists: use apply_patch for those precise/hashline edits in this plugin.\n- For diff-style or multi-line structural edits: use patch if available, otherwise use apply_patch.\n- In this plugin, edit/apply_patch support hashline refs (lineRef, endLineRef, operation, content)\n</critical_rule>\n\n<critical_rule priority=\"0\">\nUPDATE_PLAN DOES NOT EXIST -> USE \"todowrite\" INSTEAD\n- NEVER use: update_plan, updatePlan\n- ALWAYS use: todowrite for ALL task/plan operations\n- Use todoread to read current plan\n- Before plan operations: Verify you're using \"todowrite\", NOT \"update_plan\"\n</critical_rule>\n</tool_replacements>\n\n<available_tools priority=\"0\">\nFile Operations:\n - write - Create new files (if available)\n - edit - Modify existing files with string replacement (version-dependent)\n - oldString must be literal text from the current file; never pass unresolved placeholders like ${TARGET_SNIPPET}\n - apply_patch - May be the edit/patch tool name in newer host builds (version-dependent)\n - hashline_read - Read lines with hashline refs (L<line>#<hash>) for deterministic edits\n - patch - Apply diff patches (version-dependent)\n - read - Read file contents\n\nSearch/Discovery:\n - grep - Search file contents\n - glob - Find files by pattern\n - list - List directories (if available)\n\nExecution:\n - bash - Run shell commands\n\nNetwork:\n - webfetch - Fetch web content\n\nTask Management:\n - todowrite - Manage tasks/plans (REPLACES update_plan)\n - todoread - Read current plan\n</available_tools>\n\n<substitution_rules priority=\"0\">\nBase instruction says: You MUST use instead:\napply_patch -> patch (preferred if available), otherwise edit/apply_patch based on actual tool list\nupdate_plan -> todowrite\nread_plan -> todoread\nabsolute paths -> relative paths\n</substitution_rules>\n\n<verification_checklist priority=\"0\">\nBefore file/plan modifications:\n1. Am I using the actual available edit tool name (edit, patch, or apply_patch)?\n2. Am I using \"todowrite\" NOT \"update_plan\"?\n3. Is this tool in the approved list above?\n4. Am I using relative paths?\n\nIf ANY answer is NO -> STOP and correct before proceeding.\n</verification_checklist>\n\n<hashline_workflow priority=\"0\">\nWhen hashline_read is available:\n1. call hashline_read on the file\n2. edit/apply_patch using lineRef/endLineRef + operation + content\n3. use oldString/newString only as fallback, and only with literal file text (no template placeholders)\n</hashline_workflow>\n\n<safety_rules priority=\"0\">\n- Never run destructive git commands (`git reset --hard`, `git checkout --`) unless explicitly requested by the user.\n- Never call `request_user_input` unless collaboration mode is explicitly Plan mode.\n</safety_rules>\n</user_instructions>";
38
- //# sourceMappingURL=codex.d.ts.map
@@ -67,4 +67,3 @@ export declare function withBodyTimeout<T>(response: Pick<Response, "body">, rea
67
67
  * If no chunk arrives within `timeoutMs`, the read is aborted and rejected.
68
68
  */
69
69
  export declare function readBodyTextGuarded(response: Response, maxBytes?: number, timeoutMs?: number): Promise<string>;
70
- //# sourceMappingURL=fetch-utils.d.ts.map
@@ -22,4 +22,3 @@ export declare function getCachedPromptPrefix(chars?: number): Promise<string |
22
22
  * Prewarm the prompt cache without blocking startup.
23
23
  */
24
24
  export declare function prewarmHostCodexPrompt(): void;
25
- //# sourceMappingURL=host-codex-prompt.d.ts.map
@@ -69,4 +69,3 @@ export declare function loadQuotaCache(): Promise<QuotaCacheData>;
69
69
  * @param baseline - The unmodified pre-edit snapshot; only changes since it are applied.
70
70
  */
71
71
  export declare function saveQuotaCache(data: QuotaCacheData, baseline?: QuotaCacheData): Promise<void>;
72
- //# sourceMappingURL=quota-cache.d.ts.map
@@ -5,6 +5,7 @@ import { logWarn } from "./logger.js";
5
5
  import { getCodexMultiAuthDir } from "./runtime-paths.js";
6
6
  import { tempPathFor } from "./temp-path.js";
7
7
  import { withFileTransactionLock } from "./storage/file-lock.js";
8
+ import { assertJsonStoreFileMtimeUnchanged, getJsonStoreFileMtimeMs, withJsonStoreCasRetry, } from "./storage/json-store-lock.js";
8
9
  import { isRecord } from "./utils.js";
9
10
  const QUOTA_CACHE_PATH = join(getCodexMultiAuthDir(), "quota-cache.json");
10
11
  const QUOTA_CACHE_LABEL = basename(QUOTA_CACHE_PATH);
@@ -171,6 +172,20 @@ function mergeQuotaChanges(current, proposed, baseline) {
171
172
  current[namespace] ??= {};
172
173
  current[namespace][key] = update;
173
174
  }
175
+ else if (same(latest, before[key])) {
176
+ // The on-disk entry is still exactly the one this caller's
177
+ // baseline saw, so nothing concurrent touched this key:
178
+ // `update` is a deliberate write whose lower stamp can only
179
+ // come from a backward clock jump on this writer. Re-stamp
180
+ // it just ahead instead of silently losing it. A raced key
181
+ // (`!same`) falls through and keeps the on-disk entry: its
182
+ // higher stamp marks a newer observation, and bumping an
183
+ // older one past it would let stale quota data overwrite
184
+ // fresher data.
185
+ update.updatedAt = latest.updatedAt + 1;
186
+ current[namespace] ??= {};
187
+ current[namespace][key] = update;
188
+ }
174
189
  }
175
190
  else if (same(latest, before[key])) {
176
191
  delete current[namespace]?.[key];
@@ -205,48 +220,57 @@ export async function saveQuotaCache(data, baseline = { byAccountId: {}, byEmail
205
220
  const proposed = structuredClone(data), before = structuredClone(baseline);
206
221
  const writeTask = async () => {
207
222
  try {
223
+ // The transaction lock gives the read-merge-write cross-process mutual
224
+ // exclusion; the mtime CAS retry inside it is the second-line guard for
225
+ // writers that do not take the lock: every attempt re-stats, re-reads
226
+ // the freshest on-disk cache, and re-merges this call's diff onto it
227
+ // (matching the config save's reload-and-retry ESTALE semantics).
208
228
  await withFileTransactionLock(QUOTA_CACHE_PATH, async () => {
209
- const merged = mergeQuotaChanges(await readQuotaCache(), proposed, before);
210
- const payload = { version: 1, ...merged };
211
- const cacheDir = getCodexMultiAuthDir();
212
- // The quota cache lives alongside other at-rest secrets, so keep the
213
- // directory owner-only on POSIX (mode is a no-op on win32 / ACL-based).
214
- await fs.mkdir(cacheDir, { recursive: true, mode: 0o700 });
215
- // mkdir's mode only applies to a freshly-created dir; an upgrade with a
216
- // pre-existing multi-auth dir keeps its old (possibly world-listable)
217
- // perms, so re-assert 0o700 on POSIX. Best-effort: a chmod failure must
218
- // not break the cache write (the 0o600 file below still protects data).
219
- if (process.platform !== "win32") {
220
- try {
221
- await fs.chmod(cacheDir, 0o700);
222
- }
223
- catch {
224
- // Best-effort hardening only.
225
- }
226
- }
227
- const tempPath = tempPathFor(QUOTA_CACHE_PATH);
228
- await fs.writeFile(tempPath, `${JSON.stringify(payload, null, 2)}\n`, {
229
- encoding: "utf8",
230
- mode: 0o600,
231
- });
232
- let renamed = false;
233
- try {
234
- await withRetry(() => fs.rename(tempPath, QUOTA_CACHE_PATH), {
235
- maxAttempts: 5,
236
- backoffMs: (attempt) => 10 * 2 ** (attempt - 1),
237
- });
238
- renamed = true;
239
- }
240
- finally {
241
- if (!renamed) {
229
+ await withJsonStoreCasRetry(async () => {
230
+ const expectedMtimeMs = await getJsonStoreFileMtimeMs(QUOTA_CACHE_PATH);
231
+ const merged = mergeQuotaChanges(await readQuotaCache(), proposed, before);
232
+ const payload = { version: 1, ...merged };
233
+ const cacheDir = getCodexMultiAuthDir();
234
+ // The quota cache lives alongside other at-rest secrets, so keep the
235
+ // directory owner-only on POSIX (mode is a no-op on win32 / ACL-based).
236
+ await fs.mkdir(cacheDir, { recursive: true, mode: 0o700 });
237
+ // mkdir's mode only applies to a freshly-created dir; an upgrade with a
238
+ // pre-existing multi-auth dir keeps its old (possibly world-listable)
239
+ // perms, so re-assert 0o700 on POSIX. Best-effort: a chmod failure must
240
+ // not break the cache write (the 0o600 file below still protects data).
241
+ if (process.platform !== "win32") {
242
242
  try {
243
- await fs.unlink(tempPath);
243
+ await fs.chmod(cacheDir, 0o700);
244
244
  }
245
245
  catch {
246
- // Best effort temp cleanup.
246
+ // Best-effort hardening only.
247
247
  }
248
248
  }
249
- }
249
+ const tempPath = tempPathFor(QUOTA_CACHE_PATH);
250
+ await fs.writeFile(tempPath, `${JSON.stringify(payload, null, 2)}\n`, {
251
+ encoding: "utf8",
252
+ mode: 0o600,
253
+ });
254
+ let renamed = false;
255
+ try {
256
+ await assertJsonStoreFileMtimeUnchanged(QUOTA_CACHE_PATH, expectedMtimeMs);
257
+ await withRetry(() => fs.rename(tempPath, QUOTA_CACHE_PATH), {
258
+ maxAttempts: 5,
259
+ backoffMs: (attempt) => 10 * 2 ** (attempt - 1),
260
+ });
261
+ renamed = true;
262
+ }
263
+ finally {
264
+ if (!renamed) {
265
+ try {
266
+ await fs.unlink(tempPath);
267
+ }
268
+ catch {
269
+ // Best effort temp cleanup.
270
+ }
271
+ }
272
+ }
273
+ });
250
274
  });
251
275
  }
252
276
  catch (error) {
@@ -85,4 +85,3 @@ export interface ProbeCodexQuotaOptions {
85
85
  * @throws If no candidate model produces a quota snapshot, throws the last encountered error or a generic failure error.
86
86
  */
87
87
  export declare function fetchCodexQuotaSnapshot(options: ProbeCodexQuotaOptions): Promise<CodexQuotaSnapshot>;
88
- //# sourceMappingURL=quota-probe.d.ts.map
@@ -4,7 +4,7 @@ import { QUOTA_PROBE_MODEL_CHAIN, resolveProbeReasoningEffort, } from "./request
4
4
  import { CodexUnavailableError, isCodexUnavailableError } from "./errors.js";
5
5
  import { getCodexInstructions } from "./prompts/codex.js";
6
6
  import { mutateRuntimeObservabilitySnapshot } from "./runtime/runtime-observability.js";
7
- import { isRecord } from "./utils.js";
7
+ import { combineSignals, isRecord } from "./utils.js";
8
8
  import { FirstUseProbeError, finishSubscriptionFirstUse, needsSubscriptionFirstUse } from "./runtime/subscription-first-use.js";
9
9
  /**
10
10
  * Human-readable note shown when a live probe fails solely because the account
@@ -371,7 +371,10 @@ export async function fetchCodexQuotaSnapshot(options) {
371
371
  method: "POST",
372
372
  headers,
373
373
  body: JSON.stringify(probeBody),
374
- signal: options.signal ? AbortSignal.any([controller.signal, options.signal]) : controller.signal,
374
+ // combineSignals wraps AbortSignal.any with null-tolerance:
375
+ // the composite detaches from a long-lived caller signal once
376
+ // this probe completes, instead of pinning a listener on it.
377
+ signal: options.signal ? combineSignals(controller.signal, options.signal) : controller.signal,
375
378
  });
376
379
  }
377
380
  finally {
@@ -21,4 +21,3 @@ export declare function quotaWorkspaceKey(account: QuotaCacheAccountRef, workspa
21
21
  export declare function quotaWorkspaceId(account: QuotaCacheAccountRef): string | undefined;
22
22
  /** Read exact-workspace quota, using legacy binding entries only for that binding. */
23
23
  export declare function findQuotaCacheEntryForAccount(cache: QuotaCacheData | null | undefined, account: QuotaCacheAccountRef, accounts: readonly QuotaCacheAccountRef[], emailFallbackState?: ReadonlyMap<string, QuotaEmailFallbackState>, workspaceId?: string | undefined): QuotaCacheEntry | null;
24
- //# sourceMappingURL=quota-readiness.d.ts.map
@@ -9,4 +9,3 @@ export declare const PART_STORAGE: string;
9
9
  export declare const THINKING_TYPES: Set<string>;
10
10
  export declare const META_TYPES: Set<string>;
11
11
  export declare const CONTENT_TYPES: Set<string>;
12
- //# sourceMappingURL=constants.d.ts.map
@@ -41,4 +41,3 @@ export declare function findEmptyMessageByIndex(sessionID: string, targetIndex:
41
41
  export declare function findMessageByIndexNeedingThinking(sessionID: string, targetIndex: number): string | null;
42
42
  export declare function replaceEmptyTextParts(messageID: string, replacementText: string): boolean;
43
43
  export declare function findMessagesWithEmptyTextParts(sessionID: string): string[];
44
- //# sourceMappingURL=storage.d.ts.map