@cleocode/core 2026.9.22 → 2026.9.23

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 (316) hide show
  1. package/dist/build-provenance.json +6 -0
  2. package/dist/decide/audit.d.ts +7 -1
  3. package/dist/decide/audit.d.ts.map +1 -1
  4. package/dist/decide/audit.js.map +1 -1
  5. package/dist/decide/client.d.ts +46 -4
  6. package/dist/decide/client.d.ts.map +1 -1
  7. package/dist/decide/client.js +328 -33
  8. package/dist/decide/client.js.map +1 -1
  9. package/dist/decide/credentials.d.ts +28 -4
  10. package/dist/decide/credentials.d.ts.map +1 -1
  11. package/dist/decide/credentials.js +50 -12
  12. package/dist/decide/credentials.js.map +1 -1
  13. package/dist/decide/index.d.ts +10 -4
  14. package/dist/decide/index.d.ts.map +1 -1
  15. package/dist/decide/index.js +10 -4
  16. package/dist/decide/index.js.map +1 -1
  17. package/dist/decide/jev-wire.d.ts +100 -8
  18. package/dist/decide/jev-wire.d.ts.map +1 -1
  19. package/dist/decide/jev-wire.js +402 -55
  20. package/dist/decide/jev-wire.js.map +1 -1
  21. package/dist/decide/operations.d.ts +97 -20
  22. package/dist/decide/operations.d.ts.map +1 -1
  23. package/dist/decide/operations.js +180 -28
  24. package/dist/decide/operations.js.map +1 -1
  25. package/dist/decide/provider-state.d.ts +89 -0
  26. package/dist/decide/provider-state.d.ts.map +1 -0
  27. package/dist/decide/provider-state.js +134 -0
  28. package/dist/decide/provider-state.js.map +1 -0
  29. package/dist/decide/provider.d.ts +26 -5
  30. package/dist/decide/provider.d.ts.map +1 -1
  31. package/dist/decide/provider.js.map +1 -1
  32. package/dist/decide/providers.d.ts +58 -0
  33. package/dist/decide/providers.d.ts.map +1 -0
  34. package/dist/decide/providers.js +69 -0
  35. package/dist/decide/providers.js.map +1 -0
  36. package/dist/decide/site.d.ts +2 -2
  37. package/dist/decide/site.d.ts.map +1 -1
  38. package/dist/decide/site.js +2 -0
  39. package/dist/decide/site.js.map +1 -1
  40. package/dist/decide/sites/list.d.ts +46 -0
  41. package/dist/decide/sites/list.d.ts.map +1 -0
  42. package/dist/decide/sites/list.js +206 -0
  43. package/dist/decide/sites/list.js.map +1 -0
  44. package/dist/decide/sites/registry.d.ts +130 -0
  45. package/dist/decide/sites/registry.d.ts.map +1 -0
  46. package/dist/decide/sites/registry.js +375 -0
  47. package/dist/decide/sites/registry.js.map +1 -0
  48. package/dist/decide/spend.d.ts +196 -0
  49. package/dist/decide/spend.d.ts.map +1 -0
  50. package/dist/decide/spend.js +387 -0
  51. package/dist/decide/spend.js.map +1 -0
  52. package/dist/decide/wizard.d.ts +53 -0
  53. package/dist/decide/wizard.d.ts.map +1 -0
  54. package/dist/decide/wizard.js +144 -0
  55. package/dist/decide/wizard.js.map +1 -0
  56. package/dist/docs/docs-audit.d.ts.map +1 -1
  57. package/dist/docs/docs-audit.js +1 -2
  58. package/dist/docs/docs-audit.js.map +1 -1
  59. package/dist/docs/export-document.js +775 -415
  60. package/dist/docs/export-document.js.map +3 -3
  61. package/dist/index.d.ts +2 -2
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +1 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/init/scaffold-workflows.d.ts.map +1 -1
  66. package/dist/init/scaffold-workflows.js +6 -3
  67. package/dist/init/scaffold-workflows.js.map +1 -1
  68. package/dist/init/upgrade-workflows.d.ts.map +1 -1
  69. package/dist/init/upgrade-workflows.js +6 -3
  70. package/dist/init/upgrade-workflows.js.map +1 -1
  71. package/dist/internal.d.ts +1 -1
  72. package/dist/internal.d.ts.map +1 -1
  73. package/dist/internal.js +1 -1
  74. package/dist/internal.js.map +1 -1
  75. package/dist/llm/plugin-facade.js +886 -526
  76. package/dist/llm/plugin-facade.js.map +3 -3
  77. package/dist/memory/decision-contradiction.d.ts +3 -3
  78. package/dist/memory/decision-contradiction.d.ts.map +1 -1
  79. package/dist/memory/decision-contradiction.js +4 -3
  80. package/dist/memory/decision-contradiction.js.map +1 -1
  81. package/dist/memory/graph-memory-bridge.d.ts.map +1 -1
  82. package/dist/memory/graph-memory-bridge.js +2 -0
  83. package/dist/memory/graph-memory-bridge.js.map +1 -1
  84. package/dist/memory/observation-type-decision.d.ts +2 -2
  85. package/dist/memory/observation-type-decision.d.ts.map +1 -1
  86. package/dist/memory/observation-type-decision.js +3 -2
  87. package/dist/memory/observation-type-decision.js.map +1 -1
  88. package/dist/memory/pipeline-manifest-sqlite.d.ts +111 -0
  89. package/dist/memory/pipeline-manifest-sqlite.d.ts.map +1 -1
  90. package/dist/memory/pipeline-manifest-sqlite.js +230 -2
  91. package/dist/memory/pipeline-manifest-sqlite.js.map +1 -1
  92. package/dist/nexus/migrations/M-split-graph.d.ts.map +1 -1
  93. package/dist/nexus/migrations/M-split-graph.js +4 -0
  94. package/dist/nexus/migrations/M-split-graph.js.map +1 -1
  95. package/dist/nexus/store.d.ts.map +1 -1
  96. package/dist/nexus/store.js +2 -0
  97. package/dist/nexus/store.js.map +1 -1
  98. package/dist/orchestrate/pivot.d.ts.map +1 -1
  99. package/dist/orchestrate/pivot.js +13 -8
  100. package/dist/orchestrate/pivot.js.map +1 -1
  101. package/dist/orchestrate/plan.d.ts.map +1 -1
  102. package/dist/orchestrate/plan.js +3 -0
  103. package/dist/orchestrate/plan.js.map +1 -1
  104. package/dist/orchestration/lead-rollup.js +14 -2
  105. package/dist/orchestration/lead-rollup.js.map +1 -1
  106. package/dist/orchestration/owner-decision-readiness.d.ts +2 -2
  107. package/dist/orchestration/owner-decision-readiness.d.ts.map +1 -1
  108. package/dist/orchestration/owner-decision-readiness.js +3 -2
  109. package/dist/orchestration/owner-decision-readiness.js.map +1 -1
  110. package/dist/orchestration/validate-spawn.d.ts.map +1 -1
  111. package/dist/orchestration/validate-spawn.js +2 -0
  112. package/dist/orchestration/validate-spawn.js.map +1 -1
  113. package/dist/project-scope.d.ts +16 -0
  114. package/dist/project-scope.d.ts.map +1 -1
  115. package/dist/project-scope.js +38 -2
  116. package/dist/project-scope.js.map +1 -1
  117. package/dist/release/github-pr.d.ts.map +1 -1
  118. package/dist/release/github-pr.js +2 -1
  119. package/dist/release/github-pr.js.map +1 -1
  120. package/dist/routing/capability-matrix.d.ts.map +1 -1
  121. package/dist/routing/capability-matrix.js +7 -0
  122. package/dist/routing/capability-matrix.js.map +1 -1
  123. package/dist/selfimprove/fix-gen.js +895 -535
  124. package/dist/selfimprove/fix-gen.js.map +3 -3
  125. package/dist/sessions/briefing.d.ts.map +1 -1
  126. package/dist/sessions/briefing.js +23 -62
  127. package/dist/sessions/briefing.js.map +1 -1
  128. package/dist/sessions/focus-state-store.d.ts +6 -1
  129. package/dist/sessions/focus-state-store.d.ts.map +1 -1
  130. package/dist/sessions/focus-state-store.js +5 -2
  131. package/dist/sessions/focus-state-store.js.map +1 -1
  132. package/dist/setup/config-json-merge.d.ts +28 -0
  133. package/dist/setup/config-json-merge.d.ts.map +1 -1
  134. package/dist/setup/config-json-merge.js +51 -0
  135. package/dist/setup/config-json-merge.js.map +1 -1
  136. package/dist/setup/index.d.ts +13 -10
  137. package/dist/setup/index.d.ts.map +1 -1
  138. package/dist/setup/index.js +15 -10
  139. package/dist/setup/index.js.map +1 -1
  140. package/dist/setup/sections/llm.js +1 -1
  141. package/dist/setup/sections/llm.js.map +1 -1
  142. package/dist/setup/sections/system-one.d.ts +28 -0
  143. package/dist/setup/sections/system-one.d.ts.map +1 -0
  144. package/dist/setup/sections/system-one.js +53 -0
  145. package/dist/setup/sections/system-one.js.map +1 -0
  146. package/dist/setup/wizard.d.ts +15 -1
  147. package/dist/setup/wizard.d.ts.map +1 -1
  148. package/dist/setup/wizard.js +15 -0
  149. package/dist/setup/wizard.js.map +1 -1
  150. package/dist/skills/prune-bundled.d.ts +40 -1
  151. package/dist/skills/prune-bundled.d.ts.map +1 -1
  152. package/dist/skills/prune-bundled.js +117 -1
  153. package/dist/skills/prune-bundled.js.map +1 -1
  154. package/dist/store/agent-registry-accessor.d.ts.map +1 -1
  155. package/dist/store/agent-registry-accessor.js +2 -0
  156. package/dist/store/agent-registry-accessor.js.map +1 -1
  157. package/dist/store/backup-pack.js +1 -1
  158. package/dist/store/backup-pack.js.map +1 -1
  159. package/dist/store/backup.d.ts +15 -1
  160. package/dist/store/backup.d.ts.map +1 -1
  161. package/dist/store/backup.js +18 -2
  162. package/dist/store/backup.js.map +1 -1
  163. package/dist/store/conduit-sqlite.d.ts.map +1 -1
  164. package/dist/store/conduit-sqlite.js +3 -0
  165. package/dist/store/conduit-sqlite.js.map +1 -1
  166. package/dist/store/credential-transfer.d.ts.map +1 -1
  167. package/dist/store/credential-transfer.js +2 -0
  168. package/dist/store/credential-transfer.js.map +1 -1
  169. package/dist/store/cross-db-cleanup.d.ts.map +1 -1
  170. package/dist/store/cross-db-cleanup.js +2 -0
  171. package/dist/store/cross-db-cleanup.js.map +1 -1
  172. package/dist/store/dual-scope-db.d.ts.map +1 -1
  173. package/dist/store/dual-scope-db.js +13 -2
  174. package/dist/store/dual-scope-db.js.map +1 -1
  175. package/dist/store/migrate-signaldock-to-conduit.d.ts.map +1 -1
  176. package/dist/store/migrate-signaldock-to-conduit.js +3 -0
  177. package/dist/store/migrate-signaldock-to-conduit.js.map +1 -1
  178. package/dist/store/migration-manager.d.ts.map +1 -1
  179. package/dist/store/migration-manager.js +37 -3
  180. package/dist/store/migration-manager.js.map +1 -1
  181. package/dist/store/migration-sqlite.d.ts.map +1 -1
  182. package/dist/store/migration-sqlite.js +2 -0
  183. package/dist/store/migration-sqlite.js.map +1 -1
  184. package/dist/store/nexus-sqlite.d.ts.map +1 -1
  185. package/dist/store/nexus-sqlite.js +3 -0
  186. package/dist/store/nexus-sqlite.js.map +1 -1
  187. package/dist/store/open-cleo-db.d.ts.map +1 -1
  188. package/dist/store/open-cleo-db.js +3 -0
  189. package/dist/store/open-cleo-db.js.map +1 -1
  190. package/dist/store/portable-bundle-import.d.ts.map +1 -1
  191. package/dist/store/portable-bundle-import.js +2 -0
  192. package/dist/store/portable-bundle-import.js.map +1 -1
  193. package/dist/store/portable-bundle-relocate.d.ts.map +1 -1
  194. package/dist/store/portable-bundle-relocate.js +2 -0
  195. package/dist/store/portable-bundle-relocate.js.map +1 -1
  196. package/dist/store/portable-bundle-scan.js +1 -1
  197. package/dist/store/portable-bundle-scan.js.map +1 -1
  198. package/dist/store/ports/domain-binding.d.ts.map +1 -1
  199. package/dist/store/ports/domain-binding.js +5 -0
  200. package/dist/store/ports/domain-binding.js.map +1 -1
  201. package/dist/store/split-brain-import.d.ts.map +1 -1
  202. package/dist/store/split-brain-import.js +2 -0
  203. package/dist/store/split-brain-import.js.map +1 -1
  204. package/dist/store/sqlite-data-accessor.js +7 -0
  205. package/dist/store/sqlite-data-accessor.js.map +1 -1
  206. package/dist/store/sqlite-native.d.ts.map +1 -1
  207. package/dist/store/sqlite-native.js +1 -0
  208. package/dist/store/sqlite-native.js.map +1 -1
  209. package/dist/store/sqlite.d.ts.map +1 -1
  210. package/dist/store/sqlite.js +5 -0
  211. package/dist/store/sqlite.js.map +1 -1
  212. package/dist/store/worktree-build-guard.d.ts +191 -0
  213. package/dist/store/worktree-build-guard.d.ts.map +1 -0
  214. package/dist/store/worktree-build-guard.js +486 -0
  215. package/dist/store/worktree-build-guard.js.map +1 -0
  216. package/dist/store/worktree-isolation-guard.d.ts +27 -0
  217. package/dist/store/worktree-isolation-guard.d.ts.map +1 -1
  218. package/dist/store/worktree-isolation-guard.js +67 -1
  219. package/dist/store/worktree-isolation-guard.js.map +1 -1
  220. package/dist/store/writer-lease.d.ts.map +1 -1
  221. package/dist/store/writer-lease.js +2 -0
  222. package/dist/store/writer-lease.js.map +1 -1
  223. package/dist/system/backup.d.ts +6 -2
  224. package/dist/system/backup.d.ts.map +1 -1
  225. package/dist/system/backup.js +6 -3
  226. package/dist/system/backup.js.map +1 -1
  227. package/dist/task-tools/index.d.ts +4 -2
  228. package/dist/task-tools/index.d.ts.map +1 -1
  229. package/dist/task-tools/index.js +3 -2
  230. package/dist/task-tools/index.js.map +1 -1
  231. package/dist/task-tools/score-task-priority.d.ts +93 -18
  232. package/dist/task-tools/score-task-priority.d.ts.map +1 -1
  233. package/dist/task-tools/score-task-priority.js +193 -81
  234. package/dist/task-tools/score-task-priority.js.map +1 -1
  235. package/dist/tasks/affected-packages.d.ts +5 -1
  236. package/dist/tasks/affected-packages.d.ts.map +1 -1
  237. package/dist/tasks/affected-packages.js +21 -3
  238. package/dist/tasks/affected-packages.js.map +1 -1
  239. package/dist/tasks/analyze.d.ts +29 -1
  240. package/dist/tasks/analyze.d.ts.map +1 -1
  241. package/dist/tasks/analyze.js +45 -34
  242. package/dist/tasks/analyze.js.map +1 -1
  243. package/dist/tasks/backfill-child-projections.d.ts.map +1 -1
  244. package/dist/tasks/backfill-child-projections.js +4 -1
  245. package/dist/tasks/backfill-child-projections.js.map +1 -1
  246. package/dist/tasks/change-set.d.ts.map +1 -1
  247. package/dist/tasks/change-set.js +16 -3
  248. package/dist/tasks/change-set.js.map +1 -1
  249. package/dist/tasks/command-line.d.ts +54 -0
  250. package/dist/tasks/command-line.d.ts.map +1 -0
  251. package/dist/tasks/command-line.js +151 -0
  252. package/dist/tasks/command-line.js.map +1 -0
  253. package/dist/tasks/component-pr.d.ts.map +1 -1
  254. package/dist/tasks/component-pr.js +97 -12
  255. package/dist/tasks/component-pr.js.map +1 -1
  256. package/dist/tasks/delete.d.ts.map +1 -1
  257. package/dist/tasks/delete.js +20 -0
  258. package/dist/tasks/delete.js.map +1 -1
  259. package/dist/tasks/duplicate-detector.d.ts +3 -3
  260. package/dist/tasks/duplicate-detector.d.ts.map +1 -1
  261. package/dist/tasks/duplicate-detector.js +4 -3
  262. package/dist/tasks/duplicate-detector.js.map +1 -1
  263. package/dist/tasks/engine-wrap-ops.d.ts +29 -0
  264. package/dist/tasks/engine-wrap-ops.d.ts.map +1 -1
  265. package/dist/tasks/engine-wrap-ops.js +40 -0
  266. package/dist/tasks/engine-wrap-ops.js.map +1 -1
  267. package/dist/tasks/engine-wrap.d.ts +1 -1
  268. package/dist/tasks/engine-wrap.d.ts.map +1 -1
  269. package/dist/tasks/engine-wrap.js +1 -1
  270. package/dist/tasks/engine-wrap.js.map +1 -1
  271. package/dist/tasks/evidence.d.ts +2 -0
  272. package/dist/tasks/evidence.d.ts.map +1 -1
  273. package/dist/tasks/evidence.js +30 -4
  274. package/dist/tasks/evidence.js.map +1 -1
  275. package/dist/tasks/gate-runner.d.ts.map +1 -1
  276. package/dist/tasks/gate-runner.js +48 -12
  277. package/dist/tasks/gate-runner.js.map +1 -1
  278. package/dist/tasks/index.d.ts +1 -1
  279. package/dist/tasks/index.d.ts.map +1 -1
  280. package/dist/tasks/index.js +1 -1
  281. package/dist/tasks/index.js.map +1 -1
  282. package/dist/tasks/ops.d.ts +1 -0
  283. package/dist/tasks/ops.d.ts.map +1 -1
  284. package/dist/tasks/ranking-audit.d.ts +122 -0
  285. package/dist/tasks/ranking-audit.d.ts.map +1 -0
  286. package/dist/tasks/ranking-audit.js +193 -0
  287. package/dist/tasks/ranking-audit.js.map +1 -0
  288. package/dist/tasks/task-analyze.d.ts.map +1 -1
  289. package/dist/tasks/task-analyze.js +19 -32
  290. package/dist/tasks/task-analyze.js.map +1 -1
  291. package/dist/tasks/task-next.d.ts +44 -2
  292. package/dist/tasks/task-next.d.ts.map +1 -1
  293. package/dist/tasks/task-next.js +80 -102
  294. package/dist/tasks/task-next.js.map +1 -1
  295. package/dist/tasks/task-ops.d.ts +1 -1
  296. package/dist/tasks/task-ops.d.ts.map +1 -1
  297. package/dist/tasks/task-ops.js +1 -1
  298. package/dist/tasks/task-ops.js.map +1 -1
  299. package/dist/tasks/tool-resolver.d.ts +7 -1
  300. package/dist/tasks/tool-resolver.d.ts.map +1 -1
  301. package/dist/tasks/tool-resolver.js +24 -10
  302. package/dist/tasks/tool-resolver.js.map +1 -1
  303. package/dist/tasks/update.d.ts +8 -2
  304. package/dist/tasks/update.d.ts.map +1 -1
  305. package/dist/tasks/update.js +28 -1
  306. package/dist/tasks/update.js.map +1 -1
  307. package/dist/telemetry/sqlite.d.ts.map +1 -1
  308. package/dist/telemetry/sqlite.js +2 -0
  309. package/dist/telemetry/sqlite.js.map +1 -1
  310. package/dist/validation/doctor/checks.d.ts.map +1 -1
  311. package/dist/validation/doctor/checks.js +2 -2
  312. package/dist/validation/doctor/checks.js.map +1 -1
  313. package/package.json +14 -14
  314. package/schemas/config.schema.json +13 -0
  315. package/templates/CLEO-INJECTION.md +1 -1
  316. package/templates/CLEO-REFERENCE.md +5 -1
@@ -0,0 +1,6 @@
1
+ {
2
+ "schema": 1,
3
+ "gitHead": "3cce27b1da36cde459965b556608b68a4591a7ee",
4
+ "builtAt": "2026-09-29T12:04:14.801Z",
5
+ "linkedWorktree": null
6
+ }
@@ -18,7 +18,7 @@ import type { DecisionAnswer, DecisionOutcome, DecisionOutcomeSource } from '@cl
18
18
  /** Project-relative path of the decision audit log. */
19
19
  export declare const DECISION_AUDIT_FILE = ".cleo/audit/decisions.jsonl";
20
20
  /** Why a fallback was used (absent for provider/cache outcomes). */
21
- export type DecisionFallbackReason = 'unconfigured' | 'invalid_request' | 'budget_exhausted' | 'budget_cooling_down' | 'budget_unavailable' | 'timeout' | 'unauthorized' | 'insufficient_credits' | 'rate_limited' | 'server_error' | 'network' | 'invalid_response' | 'provider_error';
21
+ export type DecisionFallbackReason = 'unconfigured' | 'invalid_request' | 'budget_exhausted' | 'budget_cooling_down' | 'budget_unavailable' | 'timeout' | 'unauthorized' | 'insufficient_credits' | 'key_limit_exceeded' | 'budget' | 'rate_limited' | 'overloaded' | 'server_error' | 'network' | 'invalid_response' | 'provider_error';
22
22
  /** One audited answer: the decided value plus its distribution. */
23
23
  export interface DecisionAuditAnswer {
24
24
  /** Question type. */
@@ -89,6 +89,12 @@ export interface DecisionAuditEntry {
89
89
  readonly latencyMs: number;
90
90
  /** Provider-reported cost in USD, when known. */
91
91
  readonly costUsd?: number;
92
+ /** Provider-reported cost in integer micro-dollars, when known (T12664). */
93
+ readonly costMicros?: number;
94
+ /** Account balance in integer micro-dollars reported with the answer, when known. */
95
+ readonly balanceMicros?: number;
96
+ /** Provider model checkpoint that answered, when reported. */
97
+ readonly checkpoint?: string;
92
98
  /** Model the request named, when it named one. */
93
99
  readonly model?: string;
94
100
  /** Shadow-mode comparison, when the call site recorded one. */
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/decide/audit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/decide/audit.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/decide/audit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/decide/audit.ts"],"names":[],"mappings":"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"}
@@ -11,10 +11,16 @@
11
11
  * request with the contract schema (invalid → fallback).
12
12
  * 3. In-memory LRU cache hit → `source: 'cache'`.
13
13
  * 4. Within ONE per-site deadline (default {@link DEFAULT_DECISION_TIMEOUT_MS}):
14
- * take a token from the machine-wide budget, then call the provider.
15
- * Budget denial, timeout, 401/402/429/5xx, network failure or a malformed
16
- * answer → fallback. A 429 also empties the shared budget for its
17
- * `retry-after` so no other process piles on.
14
+ * check the monthly spend cap (`./spend.ts`, D11159), take a token from the
15
+ * machine-wide request budget, then call the provider. Spend cap reached
16
+ * (`budget`), key monthly limit (`key_limit_exceeded`), budget denial,
17
+ * timeout, 401/402/403/429/5xx, network failure or a malformed answer →
18
+ * fallback. A 429 empties the shared budget for its `retry-after`; a
19
+ * 503/529 trips it as a short circuit breaker; a 403 key limit stops
20
+ * decisions until the UTC month ends. Reported cost is added to the spend
21
+ * ledger. A call aborted AFTER it was sent (deadline or caller) may still be
22
+ * billed, so its reserved estimate is committed; only a failure before send
23
+ * or an error response releases the reservation.
18
24
  * 5. Append one audit line (never containing the key or raw state).
19
25
  *
20
26
  * Provider-specific knowledge (endpoints, wire JSON) lives only in
@@ -28,8 +34,11 @@ import { type DecisionAuditSink } from './audit.js';
28
34
  import { type DecisionBudget } from './budget.js';
29
35
  import { type DecisionCache } from './cache.js';
30
36
  import { type DecisionProvider, type DecisionProviderConnection } from './provider.js';
37
+ import { type SpendLedger } from './spend.js';
31
38
  /** Default per-site deadline for the budget + provider round trip, in ms. */
32
39
  export declare const DEFAULT_DECISION_TIMEOUT_MS = 300;
40
+ /** Circuit-breaker trip after a 503/529 that carried no `retry-after`, ms. */
41
+ export declare const OVERLOADED_COOLDOWN_MS = 30000;
33
42
  /**
34
43
  * A call site's local heuristic. It must be total and fast: it answers every
35
44
  * question in the request whenever no provider answer is available.
@@ -52,6 +61,10 @@ export interface DecideOptions {
52
61
  readonly cache?: DecisionCache | null;
53
62
  /** Request budget; `null` disables. Default: the machine-wide file bucket. */
54
63
  readonly budget?: DecisionBudget | null;
64
+ /** Monthly spend ledger; `null` disables the cap. Default: the machine-wide file ledger. */
65
+ readonly spend?: SpendLedger | null;
66
+ /** Monthly cap in micro-dollars. Default: `decide.budget.monthlyMicros`, else $1. */
67
+ readonly spendCapMicros?: number;
55
68
  /** Audit sink; `null` disables. Default: `<projectRoot>/.cleo/audit/decisions.jsonl`. */
56
69
  readonly audit?: DecisionAuditSink | null;
57
70
  /** Project root for the default audit sink. Default: the resolved CLEO project root. */
@@ -76,6 +89,12 @@ export declare function _resetDecideDefaultsForTest(): void;
76
89
  * @returns Redacted state of the same shape.
77
90
  */
78
91
  export declare function redactDecisionState(state: DecisionState): DecisionState;
92
+ /**
93
+ * Cost reserved per question before a call, micro-dollars. Only a
94
+ * reservation: the provider-reported cost replaces it on commit, so this is
95
+ * never what is recorded (layahost bills 5–15 µ$ per decision, 2026-09).
96
+ */
97
+ export declare const DECISION_COST_ESTIMATE_MICROS_PER_QUESTION = 15;
79
98
  /**
80
99
  * Ask a typed decision at call site `siteId`.
81
100
  *
@@ -100,4 +119,27 @@ export declare function redactDecisionState(state: DecisionState): DecisionState
100
119
  * ```
101
120
  */
102
121
  export declare function decide(siteId: string, req: DecisionRequest, fallback: DecisionHeuristic, opts?: DecideOptions): Promise<DecisionOutcome>;
122
+ /** One request of a {@link decideBatch} call, with its own heuristic. */
123
+ export interface DecisionBatchEntry {
124
+ /** Questions and state. */
125
+ readonly req: DecisionRequest;
126
+ /** The heuristic for this request. */
127
+ readonly fallback: DecisionHeuristic;
128
+ }
129
+ /**
130
+ * Ask several typed decisions at call site `siteId` under ONE deadline.
131
+ *
132
+ * When the provider reports a batch capability (`capabilities().batch`) and
133
+ * the entries fit its limits, the uncached ones go out as a single batch call
134
+ * (one spend check, one budget token). Otherwise it degrades to sequential
135
+ * {@link decide} calls that share the remaining deadline; entries the
136
+ * deadline cannot reach fall back with reason `timeout`. Never rejects; each
137
+ * entry is audited like a single decision.
138
+ *
139
+ * @param siteId - Stable call-site identifier.
140
+ * @param entries - Requests with their heuristics.
141
+ * @param opts - Same wiring as {@link decide}; `timeoutMs` covers the whole batch.
142
+ * @returns One outcome per entry, in order.
143
+ */
144
+ export declare function decideBatch(siteId: string, entries: readonly DecisionBatchEntry[], opts?: DecideOptions): Promise<DecisionOutcome[]>;
103
145
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/decide/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,aAAa,EAInB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAGL,KAAK,iBAAiB,EAEvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAEL,KAAK,aAAa,EAGnB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAEhC,MAAM,eAAe,CAAC;AAEvB,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,eAAe,KAAK,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAEnG,kCAAkC;AAClC,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACxD,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IACrC,uFAAuF;IACvF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACtC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC,yFAAyF;IACzF,QAAQ,CAAC,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,wFAAwF;IACxF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAeD;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAGlD;AAWD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAMvE;AAuHD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,MAAM,CAC1B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,eAAe,CAAC,CAqD1B"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/decide/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EACL,KAAK,cAAc,EAGnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,aAAa,EAInB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAGL,KAAK,iBAAiB,EAEvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAEL,KAAK,aAAa,EAGnB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAGhC,MAAM,eAAe,CAAC;AAEvB,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAEpB,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE,eAAe,KAAK,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAEnG,kCAAkC;AAClC,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACxD,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IACrC,uFAAuF;IACvF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACtC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC,4FAA4F;IAC5F,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACpC,qFAAqF;IACrF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,yFAAyF;IACzF,QAAQ,CAAC,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,wFAAwF;IACxF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAwCD;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAKlD;AAWD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAMvE;AAsED;;;;GAIG;AACH,eAAO,MAAM,0CAA0C,KAAK,CAAC;AA2K7D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,MAAM,CAC1B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,eAAe,CAAC,CAyB1B;AAmED,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,2BAA2B;IAC3B,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;CACtC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,SAAS,kBAAkB,EAAE,EACtC,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,eAAe,EAAE,CAAC,CA8F5B"}
@@ -11,10 +11,16 @@
11
11
  * request with the contract schema (invalid → fallback).
12
12
  * 3. In-memory LRU cache hit → `source: 'cache'`.
13
13
  * 4. Within ONE per-site deadline (default {@link DEFAULT_DECISION_TIMEOUT_MS}):
14
- * take a token from the machine-wide budget, then call the provider.
15
- * Budget denial, timeout, 401/402/429/5xx, network failure or a malformed
16
- * answer → fallback. A 429 also empties the shared budget for its
17
- * `retry-after` so no other process piles on.
14
+ * check the monthly spend cap (`./spend.ts`, D11159), take a token from the
15
+ * machine-wide request budget, then call the provider. Spend cap reached
16
+ * (`budget`), key monthly limit (`key_limit_exceeded`), budget denial,
17
+ * timeout, 401/402/403/429/5xx, network failure or a malformed answer →
18
+ * fallback. A 429 empties the shared budget for its `retry-after`; a
19
+ * 503/529 trips it as a short circuit breaker; a 403 key limit stops
20
+ * decisions until the UTC month ends. Reported cost is added to the spend
21
+ * ledger. A call aborted AFTER it was sent (deadline or caller) may still be
22
+ * billed, so its reserved estimate is committed; only a failure before send
23
+ * or an error response releases the reservation.
18
24
  * 5. Append one audit line (never containing the key or raw state).
19
25
  *
20
26
  * Provider-specific knowledge (endpoints, wire JSON) lives only in
@@ -32,10 +38,16 @@ import { createDecisionCache, decisionCacheKey, hashCanonical, } from './cache.j
32
38
  import { loadDecideConnection } from './credentials.js';
33
39
  import { createJevProvider, JEV_ADAPTER_VERSION } from './jev-wire.js';
34
40
  import { DecisionProviderError, } from './provider.js';
41
+ import { cachedCapabilities } from './provider-state.js';
42
+ import { createFileSpendLedger, DEFAULT_MONTHLY_SPEND_CAP_MICROS, MONTHLY_SPEND_CAP_KEY, } from './spend.js';
35
43
  /** Default per-site deadline for the budget + provider round trip, in ms. */
36
44
  export const DEFAULT_DECISION_TIMEOUT_MS = 300;
45
+ /** Circuit-breaker trip after a 503/529 that carried no `retry-after`, ms. */
46
+ export const OVERLOADED_COOLDOWN_MS = 30_000;
37
47
  let defaultCache = null;
38
48
  let defaultBudget = null;
49
+ let defaultSpend = null;
50
+ let defaultSpendCap = null;
39
51
  function processCache() {
40
52
  defaultCache ??= createDecisionCache();
41
53
  return defaultCache;
@@ -44,6 +56,28 @@ function processBudget() {
44
56
  defaultBudget ??= createFileTokenBucket();
45
57
  return defaultBudget;
46
58
  }
59
+ function processSpend() {
60
+ defaultSpend ??= createFileSpendLedger();
61
+ return defaultSpend;
62
+ }
63
+ /** The configured monthly cap, read once per process; the $1 default when unset or unreadable. */
64
+ function processSpendCap(projectRoot) {
65
+ defaultSpendCap ??= (async () => {
66
+ try {
67
+ const { getConfigValue } = await import('../config/registry.js');
68
+ const value = await getConfigValue(MONTHLY_SPEND_CAP_KEY, {
69
+ projectRoot: projectRoot ?? getProjectRoot(),
70
+ });
71
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0
72
+ ? value
73
+ : DEFAULT_MONTHLY_SPEND_CAP_MICROS;
74
+ }
75
+ catch {
76
+ return DEFAULT_MONTHLY_SPEND_CAP_MICROS;
77
+ }
78
+ })();
79
+ return defaultSpendCap;
80
+ }
47
81
  /**
48
82
  * Reset the process-default cache and budget. Tests only.
49
83
  *
@@ -52,6 +86,8 @@ function processBudget() {
52
86
  export function _resetDecideDefaultsForTest() {
53
87
  defaultCache = null;
54
88
  defaultBudget = null;
89
+ defaultSpend = null;
90
+ defaultSpendCap = null;
55
91
  }
56
92
  function redactJson(value) {
57
93
  if (typeof value === 'string')
@@ -98,7 +134,8 @@ function resolveProvider(opts, connection) {
98
134
  return null;
99
135
  if (!decisionProviderConfigSchema.safeParse({ baseUrl: connection.baseUrl }).success)
100
136
  return null;
101
- return createJevProvider(connection);
137
+ const capabilities = cachedCapabilities(connection);
138
+ return createJevProvider(connection, capabilities ? { capabilities } : {});
102
139
  }
103
140
  function resolveAudit(opts) {
104
141
  if (opts.audit !== undefined)
@@ -120,17 +157,54 @@ function reasonForError(err) {
120
157
  return err.kind;
121
158
  }
122
159
  }
160
+ /**
161
+ * The gates for a call. The configured cap is read BEFORE the deadline
162
+ * starts (a cold config read is not provider wait), once per process.
163
+ */
164
+ async function resolveGates(opts) {
165
+ const budget = opts.budget === undefined ? processBudget() : opts.budget;
166
+ const spend = opts.spend === undefined ? processSpend() : opts.spend;
167
+ const capMicros = opts.spendCapMicros ??
168
+ (spend ? await processSpendCap(opts.projectRoot) : Number.POSITIVE_INFINITY);
169
+ return { budget, spend, capMicros };
170
+ }
123
171
  /** True when `outcome` is contract-valid and answers every question with the right type. */
124
172
  function coversRequest(outcome, req) {
125
173
  if (!decisionOutcomeSchema.safeParse(outcome).success)
126
174
  return false;
127
175
  return Object.entries(req.questions).every(([name, q]) => outcome.answers[name]?.type === q.type);
128
176
  }
129
- /** Budget + provider call; never rejects. */
130
- async function attempt(provider, budget, req, signal) {
131
- if (budget) {
132
- const grant = await budget.tryAcquire();
177
+ /**
178
+ * Cost reserved per question before a call, micro-dollars. Only a
179
+ * reservation: the provider-reported cost replaces it on commit, so this is
180
+ * never what is recorded (layahost bills 5–15 µ$ per decision, 2026-09).
181
+ */
182
+ export const DECISION_COST_ESTIMATE_MICROS_PER_QUESTION = 15;
183
+ /** Estimated cost of asking `reqs`. */
184
+ function estimateMicros(reqs) {
185
+ return reqs.reduce((n, r) => n + Object.keys(r.questions).length * DECISION_COST_ESTIMATE_MICROS_PER_QUESTION, 0);
186
+ }
187
+ /**
188
+ * Spend cap (check and reserve, atomically) then request budget. A
189
+ * reservation is released again when the budget refuses.
190
+ */
191
+ async function passGates(gates, estimate) {
192
+ let reservation;
193
+ if (gates.spend) {
194
+ const r = await gates.spend.reserve(gates.capMicros, estimate);
195
+ if (r.verdict === 'over_budget')
196
+ return { ok: false, reason: 'budget' };
197
+ if (r.verdict === 'key_limited')
198
+ return { ok: false, reason: 'key_limit_exceeded' };
199
+ if (r.verdict === 'unavailable')
200
+ return { ok: false, reason: 'budget_unavailable' };
201
+ reservation = r.id;
202
+ }
203
+ if (gates.budget) {
204
+ const grant = await gates.budget.tryAcquire();
133
205
  if (!grant.granted) {
206
+ if (reservation !== undefined)
207
+ await gates.spend?.release(reservation);
134
208
  return {
135
209
  ok: false,
136
210
  reason: grant.reason === 'exhausted'
@@ -141,19 +215,82 @@ async function attempt(provider, budget, req, signal) {
141
215
  };
142
216
  }
143
217
  }
144
- if (signal.aborted)
218
+ return { ok: true, ...(reservation !== undefined ? { reservation } : {}) };
219
+ }
220
+ /** Commit the reported cost against a reservation (or record it when there is none). */
221
+ async function settle(gates, reservation, micros) {
222
+ if (!gates.spend)
223
+ return;
224
+ if (reservation !== undefined)
225
+ await gates.spend.commit(reservation, micros);
226
+ else
227
+ await gates.spend.record(micros);
228
+ }
229
+ /** Side effects of a provider error on the shared gates; the fallback reason. */
230
+ async function onProviderError(err, gates) {
231
+ if (err.kind === 'rate_limited' && gates.budget)
232
+ await gates.budget.penalize(err.retryAfterMs);
233
+ if (err.kind === 'overloaded' && gates.budget) {
234
+ await gates.budget.penalize(err.retryAfterMs ?? OVERLOADED_COOLDOWN_MS);
235
+ }
236
+ if (err.kind === 'key_limit_exceeded' && gates.spend)
237
+ await gates.spend.markKeyLimited();
238
+ return reasonForError(err);
239
+ }
240
+ /**
241
+ * Whether a failed call may still have been billed: it was aborted (deadline
242
+ * or caller) AFTER the request was handed to the provider. The server may
243
+ * have answered and billed it, so its reservation is committed at the
244
+ * estimate rather than released. Every other failure is an error response or
245
+ * a transport failure, which the provider does not bill.
246
+ */
247
+ function abortedAfterSend(err, signal) {
248
+ return signal.aborted || (err instanceof DecisionProviderError && err.kind === 'aborted');
249
+ }
250
+ /**
251
+ * Settle a reservation after a failed call: commit the estimate when the
252
+ * call was aborted after send ({@link abortedAfterSend}), else release it.
253
+ */
254
+ async function settleFailure(gates, reservation, estimate, err, signal) {
255
+ if (reservation === undefined || !gates.spend)
256
+ return;
257
+ if (abortedAfterSend(err, signal))
258
+ await gates.spend.commit(reservation, estimate);
259
+ else
260
+ await gates.spend.release(reservation);
261
+ }
262
+ /** Micro-dollars an outcome reported, preferring the exact integer. */
263
+ function reportedMicros(outcome) {
264
+ if (outcome.costMicros !== undefined)
265
+ return outcome.costMicros;
266
+ return outcome.costUsd !== undefined ? Math.round(outcome.costUsd * 1e6) : 0;
267
+ }
268
+ /** Spend gate + budget + provider call; never rejects. */
269
+ async function attempt(provider, gates, req, signal) {
270
+ const estimate = estimateMicros([req]);
271
+ const gate = await passGates(gates, estimate);
272
+ if (!gate.ok)
273
+ return { ok: false, reason: gate.reason };
274
+ if (signal.aborted) {
275
+ // Pre-send: nothing left the machine, so nothing can be billed.
276
+ if (gate.reservation !== undefined)
277
+ await gates.spend?.release(gate.reservation);
145
278
  return { ok: false, reason: 'timeout' };
279
+ }
146
280
  try {
147
281
  const outcome = await provider.decide(req, signal);
282
+ // A 2xx is billed even when its body is unusable.
283
+ await settle(gates, gate.reservation, reportedMicros(outcome));
148
284
  if (!coversRequest(outcome, req))
149
285
  return { ok: false, reason: 'invalid_response' };
150
286
  return { ok: true, outcome };
151
287
  }
152
288
  catch (err) {
289
+ // Failed requests are not billed (provider docs), but one aborted after
290
+ // send may have been answered and billed: charge its estimate.
291
+ await settleFailure(gates, gate.reservation, estimate, err, signal);
153
292
  if (err instanceof DecisionProviderError) {
154
- if (err.kind === 'rate_limited' && budget)
155
- await budget.penalize(err.retryAfterMs);
156
- return { ok: false, reason: reasonForError(err) };
293
+ return { ok: false, reason: await onProviderError(err, gates) };
157
294
  }
158
295
  return { ok: false, reason: signal.aborted ? 'timeout' : 'provider_error' };
159
296
  }
@@ -163,7 +300,7 @@ async function attempt(provider, budget, req, signal) {
163
300
  * deadline or the caller's signal fires, even if the provider ignores its
164
301
  * abort signal.
165
302
  */
166
- function withDeadline(run, timeoutMs, callerSignal) {
303
+ function withDeadline(run, timeoutMs, callerSignal, timedOut, crashed) {
167
304
  const controller = new AbortController();
168
305
  return new Promise((resolve) => {
169
306
  let settled = false;
@@ -177,7 +314,7 @@ function withDeadline(run, timeoutMs, callerSignal) {
177
314
  };
178
315
  const onTimeout = () => {
179
316
  controller.abort();
180
- finish({ ok: false, reason: 'timeout' });
317
+ finish(timedOut);
181
318
  };
182
319
  const onCallerAbort = () => onTimeout();
183
320
  const timer = setTimeout(onTimeout, Math.max(0, timeoutMs));
@@ -186,7 +323,7 @@ function withDeadline(run, timeoutMs, callerSignal) {
186
323
  return;
187
324
  }
188
325
  callerSignal?.addEventListener('abort', onCallerAbort, { once: true });
189
- run(controller.signal).then(finish, () => finish({ ok: false, reason: 'provider_error' }));
326
+ run(controller.signal).then(finish, () => finish(crashed));
190
327
  });
191
328
  }
192
329
  /**
@@ -213,10 +350,29 @@ function withDeadline(run, timeoutMs, callerSignal) {
213
350
  * ```
214
351
  */
215
352
  export async function decide(siteId, req, fallback, opts = {}) {
216
- const started = performance.now();
217
- const elapsed = () => Math.max(0, performance.now() - started);
218
353
  const audit = resolveAudit(opts);
219
354
  const connection = resolveConnection(opts);
355
+ const item = prepare(siteId, req, fallback, opts, audit, connection);
356
+ const provider = resolveProvider(opts, connection);
357
+ if (!provider)
358
+ return item.useFallback('unconfigured');
359
+ if (!item.valid)
360
+ return item.useFallback('invalid_request');
361
+ const cache = opts.cache === undefined ? processCache() : opts.cache;
362
+ const hit = cache?.get(item.key);
363
+ if (hit)
364
+ return item.finish({ ...hit, source: 'cache', latencyMs: item.elapsed() });
365
+ const gates = await resolveGates(opts);
366
+ const result = await withDeadline((signal) => attempt(provider, gates, item.sent, signal), opts.timeoutMs ?? DEFAULT_DECISION_TIMEOUT_MS, opts.signal, { ok: false, reason: 'timeout' }, { ok: false, reason: 'provider_error' });
367
+ if (!result.ok)
368
+ return item.useFallback(result.reason);
369
+ cache?.set(item.key, result.outcome);
370
+ return item.finish({ ...result.outcome, source: 'provider', latencyMs: item.elapsed() });
371
+ }
372
+ /** Redact, validate, key and wire the audit for one request. */
373
+ function prepare(siteId, req, fallback, opts, audit, connection) {
374
+ const started = performance.now();
375
+ const elapsed = () => Math.max(0, performance.now() - started);
220
376
  const defaultModel = req.model === undefined ? connection?.model : undefined;
221
377
  const sent = {
222
378
  ...req,
@@ -237,26 +393,165 @@ export async function decide(siteId, req, fallback, opts = {}) {
237
393
  ...(reason ? { fallbackReason: reason } : {}),
238
394
  latencyMs: outcome.latencyMs,
239
395
  ...(outcome.costUsd !== undefined ? { costUsd: outcome.costUsd } : {}),
396
+ ...(outcome.costMicros !== undefined ? { costMicros: outcome.costMicros } : {}),
397
+ ...(outcome.balanceMicros !== undefined ? { balanceMicros: outcome.balanceMicros } : {}),
398
+ ...(outcome.checkpoint !== undefined ? { checkpoint: outcome.checkpoint } : {}),
240
399
  ...(sent.model !== undefined ? { model: sent.model } : {}),
241
400
  });
242
401
  return outcome;
243
402
  };
244
- const useFallback = (reason) => finish({ answers: fallback(req), source: 'fallback', latencyMs: elapsed() }, reason);
403
+ return {
404
+ sent,
405
+ valid: decisionRequestSchema.safeParse(sent).success,
406
+ key: decisionCacheKey(opts.adapterVersion ?? JEV_ADAPTER_VERSION, sent),
407
+ elapsed,
408
+ finish,
409
+ useFallback: (reason) => finish({ answers: fallback(req), source: 'fallback', latencyMs: elapsed() }, reason),
410
+ };
411
+ }
412
+ /**
413
+ * Ask several typed decisions at call site `siteId` under ONE deadline.
414
+ *
415
+ * When the provider reports a batch capability (`capabilities().batch`) and
416
+ * the entries fit its limits, the uncached ones go out as a single batch call
417
+ * (one spend check, one budget token). Otherwise it degrades to sequential
418
+ * {@link decide} calls that share the remaining deadline; entries the
419
+ * deadline cannot reach fall back with reason `timeout`. Never rejects; each
420
+ * entry is audited like a single decision.
421
+ *
422
+ * @param siteId - Stable call-site identifier.
423
+ * @param entries - Requests with their heuristics.
424
+ * @param opts - Same wiring as {@link decide}; `timeoutMs` covers the whole batch.
425
+ * @returns One outcome per entry, in order.
426
+ */
427
+ export async function decideBatch(siteId, entries, opts = {}) {
428
+ if (entries.length === 0)
429
+ return [];
430
+ const started = performance.now();
431
+ const deadlineMs = opts.timeoutMs ?? DEFAULT_DECISION_TIMEOUT_MS;
432
+ const remaining = () => Math.max(0, deadlineMs - (performance.now() - started));
433
+ const connection = resolveConnection(opts);
245
434
  const provider = resolveProvider(opts, connection);
246
- if (!provider)
247
- return useFallback('unconfigured');
248
- if (!decisionRequestSchema.safeParse(sent).success)
249
- return useFallback('invalid_request');
435
+ const limits = provider?.capabilities?.().batch;
436
+ const questions = entries.reduce((n, e) => n + Object.keys(e.req.questions).length, 0);
437
+ const batchable = provider !== null &&
438
+ provider.decideBatch !== undefined &&
439
+ limits !== undefined &&
440
+ entries.length > 1 &&
441
+ entries.length <= limits.maxRequests &&
442
+ questions <= limits.maxQuestions;
443
+ if (!batchable || !provider?.decideBatch) {
444
+ const out = [];
445
+ for (const entry of entries) {
446
+ const timeoutMs = remaining();
447
+ // model-site-allowed: plumbing — the caller's siteId is checked at its decideBatch call (gate 35)
448
+ const outcome = await decide(siteId, entry.req, entry.fallback, { ...opts, timeoutMs });
449
+ out.push(outcome);
450
+ }
451
+ return out;
452
+ }
453
+ const audit = resolveAudit(opts);
250
454
  const cache = opts.cache === undefined ? processCache() : opts.cache;
251
- const key = decisionCacheKey(opts.adapterVersion ?? JEV_ADAPTER_VERSION, sent);
252
- const hit = cache?.get(key);
253
- if (hit)
254
- return finish({ ...hit, source: 'cache', latencyMs: elapsed() });
255
- const budget = opts.budget === undefined ? processBudget() : opts.budget;
256
- const result = await withDeadline((signal) => attempt(provider, budget, sent, signal), opts.timeoutMs ?? DEFAULT_DECISION_TIMEOUT_MS, opts.signal);
257
- if (!result.ok)
258
- return useFallback(result.reason);
259
- cache?.set(key, result.outcome);
260
- return finish({ ...result.outcome, source: 'provider', latencyMs: elapsed() });
455
+ const items = entries.map((e) => prepare(siteId, e.req, e.fallback, opts, audit, connection));
456
+ const out = items.map((item) => {
457
+ if (!item.valid)
458
+ return item.useFallback('invalid_request');
459
+ const hit = cache?.get(item.key);
460
+ return hit ? item.finish({ ...hit, source: 'cache', latencyMs: item.elapsed() }) : undefined;
461
+ });
462
+ const pending = items.flatMap((item, i) => (out[i] === undefined ? [{ item, i }] : []));
463
+ if (pending.length > 0) {
464
+ const gates = await resolveGates(opts);
465
+ const batch = provider.decideBatch.bind(provider);
466
+ const result = await withDeadline(async (signal) => {
467
+ const sent = pending.map((p) => p.item.sent);
468
+ const estimate = estimateMicros(sent);
469
+ const gate = await passGates(gates, estimate);
470
+ if (!gate.ok)
471
+ return { ok: false, reason: gate.reason };
472
+ if (signal.aborted) {
473
+ if (gate.reservation !== undefined)
474
+ await gates.spend?.release(gate.reservation);
475
+ return { ok: false, reason: 'timeout' };
476
+ }
477
+ try {
478
+ const answered = await batch(sent, signal);
479
+ const billed = answered.reduce((n, a) => n + (a.ok ? reportedMicros(a.outcome) : 0), 0);
480
+ await settle(gates, gate.reservation, billed);
481
+ return { ok: true, items: answered };
482
+ }
483
+ catch (err) {
484
+ await settleFailure(gates, gate.reservation, estimate, err, signal);
485
+ if (err instanceof DecisionProviderError) {
486
+ return { ok: false, reason: await onProviderError(err, gates) };
487
+ }
488
+ return { ok: false, reason: signal.aborted ? 'timeout' : 'provider_error' };
489
+ }
490
+ }, remaining(), opts.signal, { ok: false, reason: 'timeout' }, { ok: false, reason: 'provider_error' });
491
+ if (result.ok)
492
+ await applyBatchItemErrors(result.items, gates);
493
+ for (const [k, { item, i }] of pending.entries()) {
494
+ if (!result.ok) {
495
+ out[i] = item.useFallback(result.reason);
496
+ continue;
497
+ }
498
+ const answered = result.items[k];
499
+ if (answered?.ok && coversRequest(answered.outcome, item.sent)) {
500
+ cache?.set(item.key, answered.outcome);
501
+ out[i] = item.finish({
502
+ ...answered.outcome,
503
+ source: 'provider',
504
+ latencyMs: item.elapsed(),
505
+ });
506
+ }
507
+ else {
508
+ out[i] = item.useFallback(answered && !answered.ok ? batchItemReason(answered.errorKind) : 'invalid_response');
509
+ }
510
+ }
511
+ }
512
+ return items.map((item, i) => out[i] ?? item.useFallback('provider_error'));
513
+ }
514
+ /** Fallback reason for a failed batch item. */
515
+ function batchItemReason(kind) {
516
+ switch (kind) {
517
+ case 'unauthorized':
518
+ case 'insufficient_credits':
519
+ case 'key_limit_exceeded':
520
+ case 'rate_limited':
521
+ case 'overloaded':
522
+ case 'server_error':
523
+ case 'invalid_request':
524
+ case 'invalid_response':
525
+ return kind;
526
+ default:
527
+ return 'provider_error';
528
+ }
529
+ }
530
+ /** Batch item error kinds with a side effect on the shared gates. */
531
+ const GATE_ERROR_KINDS = new Set(['key_limit_exceeded', 'rate_limited', 'overloaded']);
532
+ /** The gate-affecting provider error kind named by a batch item, if any. */
533
+ function gateErrorKind(kind) {
534
+ for (const k of GATE_ERROR_KINDS)
535
+ if (k === kind)
536
+ return k;
537
+ return null;
538
+ }
539
+ /**
540
+ * Give failed batch items the same gate side effects as a failed single
541
+ * call (key-limit stop, rate-limit and overload back-off), once per kind.
542
+ */
543
+ async function applyBatchItemErrors(items, gates) {
544
+ const seen = new Set();
545
+ for (const item of items) {
546
+ if (item.ok)
547
+ continue;
548
+ const kind = gateErrorKind(item.errorKind);
549
+ if (kind === null || seen.has(kind))
550
+ continue;
551
+ seen.add(kind);
552
+ await onProviderError(new DecisionProviderError(kind, `batch item failed: ${kind}`, {
553
+ ...(item.status !== undefined ? { status: item.status } : {}),
554
+ }), gates);
555
+ }
261
556
  }
262
557
  //# sourceMappingURL=client.js.map