@sixb/core 0.1.2 → 0.1.4

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 (383) hide show
  1. package/dist/actions/index.js +19 -17
  2. package/dist/actions/index.js.map +1 -1
  3. package/dist/actions/worker.js +4 -4
  4. package/dist/actions/worker.js.map +1 -1
  5. package/dist/agents/adapters.d.ts +11 -1
  6. package/dist/agents/adapters.d.ts.map +1 -1
  7. package/dist/agents/context-compaction.d.ts +40 -0
  8. package/dist/agents/context-compaction.d.ts.map +1 -0
  9. package/dist/agents/context.js +1 -1
  10. package/dist/agents/errors.d.ts +1 -1
  11. package/dist/agents/errors.d.ts.map +1 -1
  12. package/dist/agents/execution.d.ts +3 -0
  13. package/dist/agents/execution.d.ts.map +1 -1
  14. package/dist/agents/ids.d.ts +2 -0
  15. package/dist/agents/ids.d.ts.map +1 -1
  16. package/dist/agents/index.d.ts +10 -6
  17. package/dist/agents/index.d.ts.map +1 -1
  18. package/dist/agents/index.js +51 -35
  19. package/dist/agents/index.js.map +1 -1
  20. package/dist/agents/request.d.ts.map +1 -1
  21. package/dist/agents/streams.d.ts +17 -1
  22. package/dist/agents/streams.d.ts.map +1 -1
  23. package/dist/agents/streams.js +5 -3
  24. package/dist/agents/streams.js.map +1 -1
  25. package/dist/agents/thread-context-projection.d.ts +19 -0
  26. package/dist/agents/thread-context-projection.d.ts.map +1 -0
  27. package/dist/agents/tool-definition.d.ts.map +1 -1
  28. package/dist/agents/tool-result.d.ts +6 -0
  29. package/dist/agents/tool-result.d.ts.map +1 -0
  30. package/dist/agents/types.d.ts +46 -1
  31. package/dist/agents/types.d.ts.map +1 -1
  32. package/dist/agents/validation.d.ts +2 -2
  33. package/dist/agents/validation.d.ts.map +1 -1
  34. package/dist/auth/index.js +11 -5
  35. package/dist/auth/index.js.map +1 -1
  36. package/dist/authorization/index.js +6 -2
  37. package/dist/authorization/index.js.map +1 -1
  38. package/dist/bootstrap/index.js +33 -31
  39. package/dist/bootstrap/index.js.map +1 -1
  40. package/dist/{chunk-ys1nse2v.js → chunk-1wk20wn3.js} +438 -246
  41. package/dist/{chunk-ys1nse2v.js.map → chunk-1wk20wn3.js.map} +11 -11
  42. package/dist/{chunk-sa4340y1.js → chunk-1yz3x65y.js} +7 -7
  43. package/dist/{chunk-gqdd7508.js → chunk-4rh2m6zg.js} +1 -1
  44. package/dist/{chunk-fmx1sz2h.js → chunk-72q2y8hn.js} +3 -3
  45. package/dist/{chunk-fmx1sz2h.js.map → chunk-72q2y8hn.js.map} +4 -4
  46. package/dist/{chunk-nnyp53zt.js → chunk-7gw7mepb.js} +259 -9
  47. package/dist/chunk-7gw7mepb.js.map +14 -0
  48. package/dist/{chunk-nten49ta.js → chunk-819wnjz8.js} +11 -5
  49. package/dist/chunk-819wnjz8.js.map +12 -0
  50. package/dist/chunk-96swp2wd.js +47 -0
  51. package/dist/chunk-96swp2wd.js.map +10 -0
  52. package/dist/{chunk-4x43bkbp.js → chunk-a09jx26t.js} +1 -1
  53. package/dist/{chunk-st4xgxey.js → chunk-afjpa2hs.js} +205 -48
  54. package/dist/chunk-afjpa2hs.js.map +45 -0
  55. package/dist/{chunk-0mbx247x.js → chunk-c43qr6s0.js} +427 -99
  56. package/dist/chunk-c43qr6s0.js.map +46 -0
  57. package/dist/{chunk-zg9yryhz.js → chunk-cbkm2vam.js} +3 -3
  58. package/dist/{chunk-57zpb99z.js → chunk-dm72y8wt.js} +26 -3
  59. package/dist/chunk-dm72y8wt.js.map +10 -0
  60. package/dist/chunk-drjqm1aa.js +913 -0
  61. package/dist/chunk-drjqm1aa.js.map +15 -0
  62. package/dist/chunk-dysw7nsm.js.map +1 -1
  63. package/dist/{chunk-8ryen3p3.js → chunk-f6erf2vv.js} +1 -1
  64. package/dist/{chunk-34bqh420.js → chunk-f83ka50g.js} +188 -44
  65. package/dist/chunk-f83ka50g.js.map +23 -0
  66. package/dist/{chunk-bct7hmfc.js → chunk-g13869yf.js} +3 -3
  67. package/dist/{chunk-0k4avqr2.js → chunk-hbeh8j9w.js} +2 -2
  68. package/dist/{chunk-qvybnnpe.js → chunk-nzw7sm3b.js} +2 -2
  69. package/dist/{chunk-k9pts48t.js → chunk-pn8n8sba.js} +16 -15
  70. package/dist/{chunk-k9pts48t.js.map → chunk-pn8n8sba.js.map} +3 -3
  71. package/dist/{chunk-63te770v.js → chunk-qewtks41.js} +8 -8
  72. package/dist/{chunk-63te770v.js.map → chunk-qewtks41.js.map} +2 -2
  73. package/dist/{chunk-85j5ybfj.js → chunk-rdeagz6b.js} +2 -2
  74. package/dist/{chunk-85j5ybfj.js.map → chunk-rdeagz6b.js.map} +3 -3
  75. package/dist/chunk-rhrbz7d4.js +36 -0
  76. package/dist/chunk-rhrbz7d4.js.map +10 -0
  77. package/dist/{chunk-bh6k3nc6.js → chunk-rs1wbvy6.js} +1 -1
  78. package/dist/{chunk-88ppkr54.js → chunk-rte5sm3z.js} +26 -2
  79. package/dist/chunk-rte5sm3z.js.map +12 -0
  80. package/dist/{chunk-n8herkry.js → chunk-s31d0qmq.js} +57 -20
  81. package/dist/{chunk-n8herkry.js.map → chunk-s31d0qmq.js.map} +8 -7
  82. package/dist/{chunk-7pc8b818.js → chunk-sxwk41qa.js} +1 -1
  83. package/dist/{chunk-663csy96.js → chunk-t0jjkep3.js} +2 -1
  84. package/dist/chunk-t0jjkep3.js.map +10 -0
  85. package/dist/{chunk-c6qd89g5.js → chunk-tek1qwgm.js} +6 -6
  86. package/dist/{chunk-c6qd89g5.js.map → chunk-tek1qwgm.js.map} +1 -1
  87. package/dist/{chunk-jaj13078.js → chunk-tkdscva6.js} +3 -33
  88. package/dist/{chunk-jaj13078.js.map → chunk-tkdscva6.js.map} +3 -4
  89. package/dist/{chunk-5egfka8j.js → chunk-tycbzedm.js} +2 -2
  90. package/dist/{chunk-g9t3kdtx.js → chunk-x2yw73hy.js} +1 -1
  91. package/dist/{chunk-57r1cpxx.js → chunk-xc7hq65b.js} +3 -2
  92. package/dist/{chunk-57r1cpxx.js.map → chunk-xc7hq65b.js.map} +3 -3
  93. package/dist/chunk-xdf9yswn.js +133 -0
  94. package/dist/chunk-xdf9yswn.js.map +11 -0
  95. package/dist/execution/agent.js +29 -27
  96. package/dist/execution/agent.js.map +1 -1
  97. package/dist/execution/primitive.js +29 -27
  98. package/dist/execution/primitive.js.map +2 -2
  99. package/dist/execution/request.js +30 -28
  100. package/dist/execution/request.js.map +2 -2
  101. package/dist/http/index.js +1 -1
  102. package/dist/index.d.ts +6 -5
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +60 -58
  105. package/dist/index.js.map +1 -1
  106. package/dist/lake-storage/types.d.ts +2 -1
  107. package/dist/lake-storage/types.d.ts.map +1 -1
  108. package/dist/materialization/model.d.ts +4 -0
  109. package/dist/materialization/model.d.ts.map +1 -1
  110. package/dist/materializer/edits/apply.d.ts +6 -1
  111. package/dist/materializer/edits/apply.d.ts.map +1 -1
  112. package/dist/materializer/edits/working-state.d.ts +2 -0
  113. package/dist/materializer/edits/working-state.d.ts.map +1 -1
  114. package/dist/materializer/effective/resolve.d.ts +3 -1
  115. package/dist/materializer/effective/resolve.d.ts.map +1 -1
  116. package/dist/materializer/execution/work-records.d.ts.map +1 -1
  117. package/dist/materializer/index.js +19 -17
  118. package/dist/materializer/index.js.map +1 -1
  119. package/dist/materializer/projections/replacement-plan.d.ts.map +1 -1
  120. package/dist/materializer/shared/normalize.d.ts.map +1 -1
  121. package/dist/models/index.d.ts +40 -0
  122. package/dist/models/index.d.ts.map +1 -0
  123. package/dist/objects/execution.d.ts +6 -1
  124. package/dist/objects/execution.d.ts.map +1 -1
  125. package/dist/objects/index.js +21 -19
  126. package/dist/objects/index.js.map +1 -1
  127. package/dist/objects/link/endpoints.d.ts.map +1 -1
  128. package/dist/objects/query/executor.d.ts +1 -0
  129. package/dist/objects/query/executor.d.ts.map +1 -1
  130. package/dist/objects/query/explain.d.ts +1 -0
  131. package/dist/objects/query/explain.d.ts.map +1 -1
  132. package/dist/objects/query/index.d.ts +3 -1
  133. package/dist/objects/query/index.d.ts.map +1 -1
  134. package/dist/objects/query/index.js +30 -5
  135. package/dist/objects/query/index.js.map +1 -1
  136. package/dist/objects/query/ir.d.ts +14 -1
  137. package/dist/objects/query/ir.d.ts.map +1 -1
  138. package/dist/objects/query/links.d.ts +25 -0
  139. package/dist/objects/query/links.d.ts.map +1 -0
  140. package/dist/objects/query/normalize.d.ts.map +1 -1
  141. package/dist/objects/query/planner.d.ts.map +1 -1
  142. package/dist/objects/query/validate.d.ts +1 -0
  143. package/dist/objects/query/validate.d.ts.map +1 -1
  144. package/dist/pipelines/index.js +16 -15
  145. package/dist/pipelines/index.js.map +1 -1
  146. package/dist/projections/builders.d.ts +52 -10
  147. package/dist/projections/builders.d.ts.map +1 -1
  148. package/dist/projections/index.d.ts +2 -1
  149. package/dist/projections/index.d.ts.map +1 -1
  150. package/dist/projections/internal.d.ts +4 -1
  151. package/dist/projections/internal.d.ts.map +1 -1
  152. package/dist/projections/internal.js +22 -17
  153. package/dist/projections/internal.js.map +1 -1
  154. package/dist/projections/run-dispatch.d.ts +1 -0
  155. package/dist/projections/run-dispatch.d.ts.map +1 -1
  156. package/dist/projections/timestamp.d.ts +3 -0
  157. package/dist/projections/timestamp.d.ts.map +1 -0
  158. package/dist/projections/types.d.ts +21 -9
  159. package/dist/projections/types.d.ts.map +1 -1
  160. package/dist/projections/validation.d.ts +6 -5
  161. package/dist/projections/validation.d.ts.map +1 -1
  162. package/dist/query.d.ts +2 -1
  163. package/dist/query.d.ts.map +1 -1
  164. package/dist/query.js +2 -2
  165. package/dist/queues/index.d.ts +1 -1
  166. package/dist/queues/index.d.ts.map +1 -1
  167. package/dist/queues/types.d.ts +7 -0
  168. package/dist/queues/types.d.ts.map +1 -1
  169. package/dist/runtime/ai-cost.d.ts +8 -0
  170. package/dist/runtime/ai-cost.d.ts.map +1 -0
  171. package/dist/runtime/create.d.ts +3 -0
  172. package/dist/runtime/create.d.ts.map +1 -1
  173. package/dist/runtime/definitions.d.ts +3 -0
  174. package/dist/runtime/definitions.d.ts.map +1 -1
  175. package/dist/runtime/host.d.ts +2 -0
  176. package/dist/runtime/host.d.ts.map +1 -1
  177. package/dist/runtime/resolve-definitions.d.ts +2 -0
  178. package/dist/runtime/resolve-definitions.d.ts.map +1 -1
  179. package/dist/storage/agents/in-memory.d.ts +22 -4
  180. package/dist/storage/agents/in-memory.d.ts.map +1 -1
  181. package/dist/storage/agents/index.d.ts +1 -1
  182. package/dist/storage/agents/index.d.ts.map +1 -1
  183. package/dist/storage/agents/provider.d.ts +31 -0
  184. package/dist/storage/agents/provider.d.ts.map +1 -1
  185. package/dist/storage/agents/provider.js +22 -7
  186. package/dist/storage/agents/provider.js.map +1 -1
  187. package/dist/storage/agents/types.d.ts +75 -4
  188. package/dist/storage/agents/types.d.ts.map +1 -1
  189. package/dist/storage/ai-cost/analytics.d.ts +61 -0
  190. package/dist/storage/ai-cost/analytics.d.ts.map +1 -0
  191. package/dist/storage/ai-cost/codec.d.ts +14 -0
  192. package/dist/storage/ai-cost/codec.d.ts.map +1 -0
  193. package/dist/storage/ai-cost/errors.d.ts +7 -0
  194. package/dist/storage/ai-cost/errors.d.ts.map +1 -0
  195. package/dist/storage/ai-cost/in-memory.d.ts +32 -0
  196. package/dist/storage/ai-cost/in-memory.d.ts.map +1 -0
  197. package/dist/storage/ai-cost/index.d.ts +6 -0
  198. package/dist/storage/ai-cost/index.d.ts.map +1 -0
  199. package/dist/storage/ai-cost/provider.d.ts +7 -0
  200. package/dist/storage/ai-cost/provider.d.ts.map +1 -0
  201. package/dist/storage/ai-cost/provider.js +132 -0
  202. package/dist/storage/ai-cost/provider.js.map +10 -0
  203. package/dist/storage/ai-cost/types.d.ts +154 -0
  204. package/dist/storage/ai-cost/types.d.ts.map +1 -0
  205. package/dist/storage/ai-cost/validation.d.ts +7 -0
  206. package/dist/storage/ai-cost/validation.d.ts.map +1 -0
  207. package/dist/storage/ai-usage/in-memory.d.ts +2 -1
  208. package/dist/storage/ai-usage/in-memory.d.ts.map +1 -1
  209. package/dist/storage/ai-usage/index.d.ts +1 -1
  210. package/dist/storage/ai-usage/index.d.ts.map +1 -1
  211. package/dist/storage/ai-usage/types.d.ts +6 -0
  212. package/dist/storage/ai-usage/types.d.ts.map +1 -1
  213. package/dist/storage/in-memory/index.d.ts +5 -0
  214. package/dist/storage/in-memory/index.d.ts.map +1 -1
  215. package/dist/storage/index.d.ts +6 -3
  216. package/dist/storage/index.d.ts.map +1 -1
  217. package/dist/storage/index.js +33 -21
  218. package/dist/storage/index.js.map +1 -1
  219. package/dist/storage/objects/in-memory.d.ts +6 -3
  220. package/dist/storage/objects/in-memory.d.ts.map +1 -1
  221. package/dist/storage/objects/index.d.ts +3 -1
  222. package/dist/storage/objects/index.d.ts.map +1 -1
  223. package/dist/storage/objects/keys.d.ts +16 -0
  224. package/dist/storage/objects/keys.d.ts.map +1 -0
  225. package/dist/storage/objects/types.d.ts +33 -6
  226. package/dist/storage/objects/types.d.ts.map +1 -1
  227. package/dist/storage/ontology/in-memory/materializations.d.ts.map +1 -1
  228. package/dist/storage/ontology/materializations.d.ts +3 -0
  229. package/dist/storage/ontology/materializations.d.ts.map +1 -1
  230. package/dist/storage/ontology/provider.js +1 -1
  231. package/dist/storage/operation-scope.d.ts.map +1 -1
  232. package/dist/storage/operation-scope.js +1 -1
  233. package/dist/storage/types.d.ts +2 -0
  234. package/dist/storage/types.d.ts.map +1 -1
  235. package/dist/storage/workflow-runs/provider.js +10 -5
  236. package/dist/storage/workflow-runs/provider.js.map +1 -1
  237. package/dist/syncs/index.js +16 -15
  238. package/dist/syncs/index.js.map +1 -1
  239. package/dist/testing/agent-storage-contract.d.ts +3 -2
  240. package/dist/testing/agent-storage-contract.d.ts.map +1 -1
  241. package/dist/testing/ai-cost-storage-contract.d.ts +12 -0
  242. package/dist/testing/ai-cost-storage-contract.d.ts.map +1 -0
  243. package/dist/testing/ai-usage-storage-contract.d.ts.map +1 -1
  244. package/dist/testing/effective-storage-contract.d.ts +1 -1
  245. package/dist/testing/effective-storage-contract.d.ts.map +1 -1
  246. package/dist/testing/index.d.ts +1 -0
  247. package/dist/testing/index.d.ts.map +1 -1
  248. package/dist/testing/index.js +2525 -1480
  249. package/dist/testing/index.js.map +10 -9
  250. package/dist/testing/lake-storage-contract.d.ts.map +1 -1
  251. package/dist/testing/materializer-storage-contract.d.ts.map +1 -1
  252. package/dist/testing/object-query-contract.d.ts +1 -1
  253. package/dist/testing/object-query-contract.d.ts.map +1 -1
  254. package/dist/webhooks/internal.js +14 -13
  255. package/dist/webhooks/internal.js.map +2 -2
  256. package/dist/workers/index.js +12 -2
  257. package/dist/workers/index.js.map +3 -3
  258. package/dist/workers/queue-worker.d.ts +2 -0
  259. package/dist/workers/queue-worker.d.ts.map +1 -1
  260. package/dist/workflows/execution.d.ts +3 -0
  261. package/dist/workflows/execution.d.ts.map +1 -1
  262. package/dist/workflows/index.js +30 -28
  263. package/dist/workflows/index.js.map +1 -1
  264. package/package.json +7 -1
  265. package/src/agents/adapters.ts +64 -23
  266. package/src/agents/builders.ts +3 -3
  267. package/src/agents/context-compaction.ts +261 -0
  268. package/src/agents/errors.ts +2 -2
  269. package/src/agents/execution.ts +14 -2
  270. package/src/agents/ids.ts +5 -0
  271. package/src/agents/index.ts +32 -6
  272. package/src/agents/request.ts +5 -0
  273. package/src/agents/streams.ts +56 -0
  274. package/src/agents/thread-context-projection.ts +93 -0
  275. package/src/agents/tool-definition.ts +17 -2
  276. package/src/agents/tool-result.ts +61 -0
  277. package/src/agents/types.ts +57 -1
  278. package/src/agents/validation.ts +36 -6
  279. package/src/http/api-routes.ts +7 -7
  280. package/src/index.ts +27 -0
  281. package/src/lake-storage/types.ts +2 -1
  282. package/src/materialization/model.ts +4 -0
  283. package/src/materializer/edits/apply.ts +55 -2
  284. package/src/materializer/edits/operations.ts +8 -1
  285. package/src/materializer/edits/working-state.ts +7 -0
  286. package/src/materializer/effective/resolve.ts +78 -9
  287. package/src/materializer/execution/work-records.ts +3 -1
  288. package/src/materializer/projections/entry-validator.ts +23 -1
  289. package/src/materializer/projections/replacement-plan.ts +2 -0
  290. package/src/materializer/shared/normalize.ts +16 -0
  291. package/src/materializer/telemetry/append.ts +33 -16
  292. package/src/models/index.ts +71 -0
  293. package/src/objects/execution.ts +20 -0
  294. package/src/objects/link/endpoints.ts +2 -1
  295. package/src/objects/query/executor.ts +42 -11
  296. package/src/objects/query/explain.ts +11 -0
  297. package/src/objects/query/index.ts +6 -0
  298. package/src/objects/query/ir.ts +16 -0
  299. package/src/objects/query/links.ts +308 -0
  300. package/src/objects/query/normalize.ts +40 -1
  301. package/src/objects/query/planner.ts +5 -0
  302. package/src/objects/query/validate.ts +58 -2
  303. package/src/projections/builders.ts +320 -50
  304. package/src/projections/index.ts +7 -0
  305. package/src/projections/internal.ts +4 -1
  306. package/src/projections/ownership.ts +5 -5
  307. package/src/projections/registry.ts +16 -3
  308. package/src/projections/revision.ts +35 -5
  309. package/src/projections/run-dispatch.ts +10 -1
  310. package/src/projections/timestamp.ts +66 -0
  311. package/src/projections/types.ts +20 -9
  312. package/src/projections/validation.ts +85 -19
  313. package/src/query.ts +2 -0
  314. package/src/queues/index.ts +1 -0
  315. package/src/queues/types.ts +8 -0
  316. package/src/runtime/ai-cost.ts +15 -0
  317. package/src/runtime/create.ts +4 -0
  318. package/src/runtime/definitions.ts +3 -0
  319. package/src/runtime/host.ts +2 -0
  320. package/src/runtime/resolve-definitions.ts +28 -0
  321. package/src/storage/agents/in-memory.ts +186 -4
  322. package/src/storage/agents/index.ts +5 -0
  323. package/src/storage/agents/provider.ts +225 -0
  324. package/src/storage/agents/types.ts +82 -3
  325. package/src/storage/ai-cost/analytics.ts +602 -0
  326. package/src/storage/ai-cost/codec.ts +136 -0
  327. package/src/storage/ai-cost/errors.ts +12 -0
  328. package/src/storage/ai-cost/in-memory.ts +164 -0
  329. package/src/storage/ai-cost/index.ts +35 -0
  330. package/src/storage/ai-cost/provider.ts +20 -0
  331. package/src/storage/ai-cost/types.ts +193 -0
  332. package/src/storage/ai-cost/validation.ts +337 -0
  333. package/src/storage/ai-usage/in-memory.ts +23 -0
  334. package/src/storage/ai-usage/index.ts +1 -0
  335. package/src/storage/ai-usage/types.ts +10 -0
  336. package/src/storage/in-memory/index.ts +50 -0
  337. package/src/storage/index.ts +48 -0
  338. package/src/storage/objects/in-memory.ts +103 -16
  339. package/src/storage/objects/index.ts +9 -0
  340. package/src/storage/objects/keys.ts +36 -0
  341. package/src/storage/objects/types.ts +34 -6
  342. package/src/storage/ontology/in-memory/materializations.ts +1 -0
  343. package/src/storage/ontology/materializations.ts +3 -0
  344. package/src/storage/ontology/provider-header-validation.ts +2 -2
  345. package/src/storage/operation-scope.ts +1 -0
  346. package/src/storage/types.ts +2 -0
  347. package/src/testing/agent-storage-contract.ts +353 -2
  348. package/src/testing/ai-cost-storage-contract.ts +320 -0
  349. package/src/testing/ai-usage-storage-contract.ts +35 -0
  350. package/src/testing/effective-storage-contract.ts +194 -4
  351. package/src/testing/index.ts +5 -0
  352. package/src/testing/lake-storage-contract.ts +56 -0
  353. package/src/testing/materializer-storage-contract.ts +120 -2
  354. package/src/testing/object-query-contract.ts +208 -1
  355. package/src/workers/queue-worker.ts +14 -1
  356. package/src/workflows/execution.ts +31 -10
  357. package/dist/agents/prompt.d.ts +0 -15
  358. package/dist/agents/prompt.d.ts.map +0 -1
  359. package/dist/chunk-0mbx247x.js.map +0 -43
  360. package/dist/chunk-34bqh420.js.map +0 -23
  361. package/dist/chunk-57zpb99z.js.map +0 -10
  362. package/dist/chunk-663csy96.js.map +0 -10
  363. package/dist/chunk-6wetht33.js +0 -89
  364. package/dist/chunk-6wetht33.js.map +0 -10
  365. package/dist/chunk-88ppkr54.js.map +0 -12
  366. package/dist/chunk-nnyp53zt.js.map +0 -13
  367. package/dist/chunk-nten49ta.js.map +0 -12
  368. package/dist/chunk-st4xgxey.js.map +0 -45
  369. package/dist/chunk-vp1vc6ee.js +0 -35
  370. package/dist/chunk-vp1vc6ee.js.map +0 -11
  371. package/src/agents/prompt.ts +0 -57
  372. /package/dist/{chunk-sa4340y1.js.map → chunk-1yz3x65y.js.map} +0 -0
  373. /package/dist/{chunk-gqdd7508.js.map → chunk-4rh2m6zg.js.map} +0 -0
  374. /package/dist/{chunk-4x43bkbp.js.map → chunk-a09jx26t.js.map} +0 -0
  375. /package/dist/{chunk-zg9yryhz.js.map → chunk-cbkm2vam.js.map} +0 -0
  376. /package/dist/{chunk-8ryen3p3.js.map → chunk-f6erf2vv.js.map} +0 -0
  377. /package/dist/{chunk-bct7hmfc.js.map → chunk-g13869yf.js.map} +0 -0
  378. /package/dist/{chunk-0k4avqr2.js.map → chunk-hbeh8j9w.js.map} +0 -0
  379. /package/dist/{chunk-qvybnnpe.js.map → chunk-nzw7sm3b.js.map} +0 -0
  380. /package/dist/{chunk-bh6k3nc6.js.map → chunk-rs1wbvy6.js.map} +0 -0
  381. /package/dist/{chunk-7pc8b818.js.map → chunk-sxwk41qa.js.map} +0 -0
  382. /package/dist/{chunk-5egfka8j.js.map → chunk-tycbzedm.js.map} +0 -0
  383. /package/dist/{chunk-g9t3kdtx.js.map → chunk-x2yw73hy.js.map} +0 -0
@@ -0,0 +1,602 @@
1
+ import {
2
+ type AiModelCallUsage,
3
+ type AiModelCallUsageRecord,
4
+ aggregateAiModelCallUsage,
5
+ normalizeAiModelCallUsage,
6
+ } from "../ai-usage"
7
+ import type {
8
+ AiAccountingAgentBreakdown,
9
+ AiAccountingAggregate,
10
+ AiAccountingAttribution,
11
+ AiAccountingBucket,
12
+ AiAccountingModelBreakdown,
13
+ AiAccountingOverview,
14
+ AiAccountingTimeBucket,
15
+ AiAccountingWorkflowBreakdown,
16
+ AiCostSummary,
17
+ AiModelCallAccountingItem,
18
+ AiModelCallCostRecord,
19
+ AiMoney,
20
+ ListAiModelCallAccountingInput,
21
+ ListAiModelCallAccountingResult,
22
+ QueryAiAccountingOverviewInput,
23
+ } from "./types"
24
+
25
+ const DEFAULT_MODEL_CALL_PAGE_SIZE = 50
26
+ const MAX_MODEL_CALL_PAGE_SIZE = 200
27
+ const MAX_CHART_BUCKETS = 10_000
28
+
29
+ /** One joined immutable model-call row used by in-memory analytics and bounded SQL pages. */
30
+ export interface AiAccountingRecordSetItem {
31
+ readonly usage: AiModelCallUsageRecord
32
+ readonly attribution?: AiAccountingAttribution
33
+ readonly cost?: AiModelCallCostRecord
34
+ }
35
+
36
+ export interface NormalizedAiAccountingQuery {
37
+ readonly projectId: string
38
+ readonly from: Date
39
+ readonly to: Date
40
+ readonly providerId?: string
41
+ readonly modelId?: string
42
+ }
43
+
44
+ export interface NormalizedAiModelCallAccountingQuery extends NormalizedAiAccountingQuery {
45
+ readonly executionId?: string
46
+ readonly valuationStatus?: ListAiModelCallAccountingInput["valuationStatus"]
47
+ readonly limit: number
48
+ readonly offset: number
49
+ }
50
+
51
+ interface AiAccountingUsageMeterFragment {
52
+ readonly presentCallCount: string
53
+ readonly total: string | null
54
+ }
55
+
56
+ /** One SQL-aggregated currency fragment; providers merge fragments without loading model calls. */
57
+ export interface AiAccountingAggregateFragment {
58
+ readonly dimension: "totals" | "series" | "model" | "agent" | "workflow"
59
+ readonly start?: Date
60
+ readonly providerId?: string
61
+ readonly modelId?: string
62
+ readonly agentId?: string
63
+ readonly workflowId?: string
64
+ readonly modelCallCount: string
65
+ readonly usage: {
66
+ readonly reportedCallCount: string
67
+ readonly inputTokens: AiAccountingUsageMeterFragment
68
+ readonly outputTokens: AiAccountingUsageMeterFragment
69
+ readonly uncachedInputTokens: AiAccountingUsageMeterFragment
70
+ readonly cacheReadInputTokens: AiAccountingUsageMeterFragment
71
+ readonly cacheWriteInputTokens: AiAccountingUsageMeterFragment
72
+ readonly textOutputTokens: AiAccountingUsageMeterFragment
73
+ readonly reasoningOutputTokens: AiAccountingUsageMeterFragment
74
+ }
75
+ readonly costs: {
76
+ readonly amount?: AiMoney
77
+ readonly ratedCallCount: string
78
+ readonly unpriceableCallCount: string
79
+ readonly unvaluedCallCount: string
80
+ }
81
+ }
82
+
83
+ export function normalizeAiAccountingQuery(
84
+ input: Omit<QueryAiAccountingOverviewInput, "bucket"> | ListAiModelCallAccountingInput
85
+ ): NormalizedAiAccountingQuery {
86
+ assertNonBlank(input.projectId, "accounting projectId")
87
+ assertDate(input.from, "accounting from")
88
+ assertDate(input.to, "accounting to")
89
+ if (input.from.getTime() >= input.to.getTime()) {
90
+ throw new TypeError("[Sixb] AI accounting from must be before to.")
91
+ }
92
+ if (input.providerId !== undefined) assertNonBlank(input.providerId, "accounting providerId")
93
+ if (input.modelId !== undefined) assertNonBlank(input.modelId, "accounting modelId")
94
+ return {
95
+ projectId: input.projectId,
96
+ from: new Date(input.from),
97
+ to: new Date(input.to),
98
+ ...(input.providerId === undefined ? {} : { providerId: input.providerId }),
99
+ ...(input.modelId === undefined ? {} : { modelId: input.modelId }),
100
+ }
101
+ }
102
+
103
+ export function normalizeAiModelCallAccountingQuery(
104
+ input: ListAiModelCallAccountingInput
105
+ ): NormalizedAiModelCallAccountingQuery {
106
+ const query = normalizeAiAccountingQuery(input)
107
+ if (input.executionId !== undefined) {
108
+ assertNonBlank(input.executionId, "accounting executionId")
109
+ }
110
+ const { limit, offset } = normalizePage(input.limit, input.offset)
111
+ return {
112
+ ...query,
113
+ ...(input.executionId === undefined ? {} : { executionId: input.executionId }),
114
+ ...(input.valuationStatus === undefined ? {} : { valuationStatus: input.valuationStatus }),
115
+ limit,
116
+ offset,
117
+ }
118
+ }
119
+
120
+ export function aiAccountingItemMatchesQuery(
121
+ item: AiAccountingRecordSetItem,
122
+ query: NormalizedAiAccountingQuery
123
+ ): boolean {
124
+ const occurredAt = item.usage.occurredAt.getTime()
125
+ return (
126
+ item.usage.projectId === query.projectId &&
127
+ occurredAt >= query.from.getTime() &&
128
+ occurredAt < query.to.getTime() &&
129
+ (query.providerId === undefined || item.usage.providerId === query.providerId) &&
130
+ (query.modelId === undefined || item.usage.requestedModelId === query.modelId)
131
+ )
132
+ }
133
+
134
+ export function buildAiAccountingOverview(
135
+ input: QueryAiAccountingOverviewInput,
136
+ source: Iterable<AiAccountingRecordSetItem>
137
+ ): AiAccountingOverview {
138
+ const query = normalizeAiAccountingQuery(input)
139
+ assertAiAccountingBucket(input.bucket)
140
+ const items = Array.from(source).filter((item) => aiAccountingItemMatchesQuery(item, query))
141
+ const periods = accountingPeriods(query.from, query.to, input.bucket)
142
+ const periodItems = new Map<number, AiAccountingRecordSetItem[]>(
143
+ periods.map((period) => [period.start.getTime(), []])
144
+ )
145
+ const modelItems = new Map<string, AiAccountingRecordSetItem[]>()
146
+ const agentItems = new Map<string, AiAccountingRecordSetItem[]>()
147
+ const workflowItems = new Map<string, AiAccountingRecordSetItem[]>()
148
+
149
+ for (const item of items) {
150
+ const periodStart = aiAccountingBucketStart(item.usage.occurredAt, input.bucket).getTime()
151
+ periodItems.get(periodStart)?.push(item)
152
+ appendGrouped(
153
+ modelItems,
154
+ JSON.stringify([item.usage.providerId, item.usage.requestedModelId]),
155
+ item
156
+ )
157
+ if (item.attribution) appendGrouped(agentItems, item.attribution.agentId, item)
158
+ if (item.attribution?.kind === "workflowAgent") {
159
+ appendGrouped(workflowItems, item.attribution.workflowId, item)
160
+ }
161
+ }
162
+
163
+ const series: AiAccountingTimeBucket[] = periods.map((period) => ({
164
+ ...period,
165
+ ...aggregateAccountingItems(periodItems.get(period.start.getTime()) ?? []),
166
+ }))
167
+ const models: AiAccountingModelBreakdown[] = [...modelItems]
168
+ .map(([key, grouped]) => {
169
+ const [providerId, modelId] = JSON.parse(key) as [string, string]
170
+ return { providerId, modelId, ...aggregateAccountingItems(grouped) }
171
+ })
172
+ .sort(compareModelBreakdowns)
173
+ const agents: AiAccountingAgentBreakdown[] = [...agentItems]
174
+ .map(([agentId, grouped]) => ({ agentId, ...aggregateAccountingItems(grouped) }))
175
+ .sort(compareAgentBreakdowns)
176
+ const workflows: AiAccountingWorkflowBreakdown[] = [...workflowItems]
177
+ .map(([workflowId, grouped]) => ({ workflowId, ...aggregateAccountingItems(grouped) }))
178
+ .sort(compareWorkflowBreakdowns)
179
+
180
+ return structuredClone({
181
+ range: { from: query.from, to: query.to },
182
+ bucket: input.bucket,
183
+ totals: aggregateAccountingItems(items),
184
+ series,
185
+ models,
186
+ agents,
187
+ workflows,
188
+ })
189
+ }
190
+
191
+ export function buildAiAccountingOverviewFromFragments(
192
+ input: QueryAiAccountingOverviewInput,
193
+ fragments: Iterable<AiAccountingAggregateFragment>
194
+ ): AiAccountingOverview {
195
+ const query = normalizeAiAccountingQuery(input)
196
+ const periods = accountingPeriods(query.from, query.to, input.bucket)
197
+ const totals = aggregateFragmentAccumulator()
198
+ const series = new Map<number, AggregateFragmentAccumulator>(
199
+ periods.map((period) => [period.start.getTime(), aggregateFragmentAccumulator()])
200
+ )
201
+ const models = new Map<string, AggregateFragmentAccumulator>()
202
+ const agents = new Map<string, AggregateFragmentAccumulator>()
203
+ const workflows = new Map<string, AggregateFragmentAccumulator>()
204
+
205
+ for (const fragment of fragments) {
206
+ let target: AggregateFragmentAccumulator
207
+ switch (fragment.dimension) {
208
+ case "totals":
209
+ target = totals
210
+ break
211
+ case "series": {
212
+ const start = requiredFragmentDate(fragment.start, "series start")
213
+ const existing = series.get(start.getTime())
214
+ if (!existing) {
215
+ throw new TypeError("[Sixb] AI accounting SQL series returned an unexpected bucket.")
216
+ }
217
+ target = existing
218
+ break
219
+ }
220
+ case "model":
221
+ target = getFragmentAccumulator(
222
+ models,
223
+ JSON.stringify([
224
+ requiredFragmentString(fragment.providerId, "providerId"),
225
+ requiredFragmentString(fragment.modelId, "modelId"),
226
+ ])
227
+ )
228
+ break
229
+ case "agent":
230
+ target = getFragmentAccumulator(agents, requiredFragmentString(fragment.agentId, "agentId"))
231
+ break
232
+ case "workflow":
233
+ target = getFragmentAccumulator(
234
+ workflows,
235
+ requiredFragmentString(fragment.workflowId, "workflowId")
236
+ )
237
+ break
238
+ }
239
+ appendAggregateFragment(target, fragment)
240
+ }
241
+
242
+ return structuredClone({
243
+ range: { from: query.from, to: query.to },
244
+ bucket: input.bucket,
245
+ totals: finishAggregateFragment(totals),
246
+ series: periods.map((period) => ({
247
+ ...period,
248
+ ...finishAggregateFragment(series.get(period.start.getTime())!),
249
+ })),
250
+ models: [...models]
251
+ .map(([key, value]) => {
252
+ const [providerId, modelId] = JSON.parse(key) as [string, string]
253
+ return { providerId, modelId, ...finishAggregateFragment(value) }
254
+ })
255
+ .sort(compareModelBreakdowns),
256
+ agents: [...agents]
257
+ .map(([agentId, value]) => ({ agentId, ...finishAggregateFragment(value) }))
258
+ .sort(compareAgentBreakdowns),
259
+ workflows: [...workflows]
260
+ .map(([workflowId, value]) => ({ workflowId, ...finishAggregateFragment(value) }))
261
+ .sort(compareWorkflowBreakdowns),
262
+ })
263
+ }
264
+
265
+ export function buildAiModelCallAccountingList(
266
+ input: ListAiModelCallAccountingInput,
267
+ source: Iterable<AiAccountingRecordSetItem>
268
+ ): ListAiModelCallAccountingResult {
269
+ const query = normalizeAiModelCallAccountingQuery(input)
270
+ const { limit, offset } = query
271
+ const items = Array.from(source)
272
+ .filter((item) => aiAccountingItemMatchesQuery(item, query))
273
+ .map(toAccountingItem)
274
+ .filter(
275
+ (item) =>
276
+ (input.executionId === undefined || item.usage.executionId === input.executionId) &&
277
+ (input.valuationStatus === undefined || item.valuationStatus === input.valuationStatus)
278
+ )
279
+ .sort(compareAccountingItems)
280
+ const page = items.slice(offset, offset + limit)
281
+ return structuredClone({
282
+ items: page,
283
+ hasMore: offset + page.length < items.length,
284
+ total: items.length,
285
+ })
286
+ }
287
+
288
+ export function toAccountingItem(item: AiAccountingRecordSetItem): AiModelCallAccountingItem {
289
+ return {
290
+ usage: item.usage,
291
+ ...(item.attribution === undefined ? {} : { attribution: item.attribution }),
292
+ ...(item.cost === undefined ? {} : { cost: item.cost }),
293
+ valuationStatus: item.cost?.status ?? "unvalued",
294
+ }
295
+ }
296
+
297
+ export function aiAccountingBucketStart(at: Date, bucket: AiAccountingBucket): Date {
298
+ assertDate(at, "accounting bucket timestamp")
299
+ assertAiAccountingBucket(bucket)
300
+ const start = new Date(at)
301
+ start.setUTCMinutes(0, 0, 0)
302
+ if (bucket === "hour") return start
303
+ start.setUTCHours(0)
304
+ if (bucket === "day") return start
305
+ const mondayOffset = (start.getUTCDay() + 6) % 7
306
+ start.setUTCDate(start.getUTCDate() - mondayOffset)
307
+ return start
308
+ }
309
+
310
+ function aggregateAccountingItems(
311
+ items: readonly AiAccountingRecordSetItem[]
312
+ ): AiAccountingAggregate {
313
+ return {
314
+ modelCallCount: items.length,
315
+ usage: aggregateAiModelCallUsage(items.map((item) => item.usage.usage)),
316
+ costs: aggregateCosts(items),
317
+ }
318
+ }
319
+
320
+ function aggregateCosts(items: readonly AiAccountingRecordSetItem[]): AiCostSummary {
321
+ const amounts = new Map<string, bigint>()
322
+ let ratedCallCount = 0
323
+ let unpriceableCallCount = 0
324
+ let unvaluedCallCount = 0
325
+ for (const item of items) {
326
+ if (item.cost?.status === "rated") {
327
+ ratedCallCount += 1
328
+ addAmount(amounts, item.cost.money.currency, item.cost.money.amountNanos)
329
+ } else if (item.cost?.status === "unpriceable") {
330
+ unpriceableCallCount += 1
331
+ } else {
332
+ unvaluedCallCount += 1
333
+ }
334
+ }
335
+ return {
336
+ amounts: [...amounts]
337
+ .sort(([left], [right]) => left.localeCompare(right))
338
+ .map(([currency, amountNanos]) => ({ currency, amountNanos: amountNanos.toString() })),
339
+ ratedCallCount,
340
+ unpriceableCallCount,
341
+ unvaluedCallCount,
342
+ }
343
+ }
344
+
345
+ const AGGREGATE_USAGE_FIELDS = [
346
+ "inputTokens",
347
+ "outputTokens",
348
+ "uncachedInputTokens",
349
+ "cacheReadInputTokens",
350
+ "cacheWriteInputTokens",
351
+ "textOutputTokens",
352
+ "reasoningOutputTokens",
353
+ ] as const
354
+
355
+ type AggregateUsageField = (typeof AGGREGATE_USAGE_FIELDS)[number]
356
+
357
+ interface AggregateFragmentAccumulator {
358
+ modelCallCount: bigint
359
+ reportedUsageCallCount: bigint
360
+ usage: Record<AggregateUsageField, { presentCallCount: bigint; total: bigint }>
361
+ amounts: Map<string, bigint>
362
+ ratedCallCount: bigint
363
+ unpriceableCallCount: bigint
364
+ unvaluedCallCount: bigint
365
+ }
366
+
367
+ function aggregateFragmentAccumulator(): AggregateFragmentAccumulator {
368
+ return {
369
+ modelCallCount: 0n,
370
+ reportedUsageCallCount: 0n,
371
+ usage: Object.fromEntries(
372
+ AGGREGATE_USAGE_FIELDS.map((field) => [field, { presentCallCount: 0n, total: 0n }])
373
+ ) as AggregateFragmentAccumulator["usage"],
374
+ amounts: new Map(),
375
+ ratedCallCount: 0n,
376
+ unpriceableCallCount: 0n,
377
+ unvaluedCallCount: 0n,
378
+ }
379
+ }
380
+
381
+ function getFragmentAccumulator(
382
+ values: Map<string, AggregateFragmentAccumulator>,
383
+ key: string
384
+ ): AggregateFragmentAccumulator {
385
+ const existing = values.get(key)
386
+ if (existing) return existing
387
+ const created = aggregateFragmentAccumulator()
388
+ values.set(key, created)
389
+ return created
390
+ }
391
+
392
+ function appendAggregateFragment(
393
+ target: AggregateFragmentAccumulator,
394
+ fragment: AiAccountingAggregateFragment
395
+ ): void {
396
+ target.modelCallCount += naturalBigInt(fragment.modelCallCount, "modelCallCount")
397
+ target.reportedUsageCallCount += naturalBigInt(
398
+ fragment.usage.reportedCallCount,
399
+ "reported usage call count"
400
+ )
401
+ for (const field of AGGREGATE_USAGE_FIELDS) {
402
+ const source = fragment.usage[field]
403
+ const presentCallCount = naturalBigInt(source.presentCallCount, `${field} call count`)
404
+ target.usage[field].presentCallCount += presentCallCount
405
+ if (source.total !== null) {
406
+ target.usage[field].total += naturalBigInt(source.total, `${field} total`)
407
+ } else if (presentCallCount !== 0n) {
408
+ throw new TypeError(`[Sixb] AI accounting SQL ${field} total is missing.`)
409
+ }
410
+ }
411
+ target.ratedCallCount += naturalBigInt(fragment.costs.ratedCallCount, "rated call count")
412
+ target.unpriceableCallCount += naturalBigInt(
413
+ fragment.costs.unpriceableCallCount,
414
+ "unpriceable call count"
415
+ )
416
+ target.unvaluedCallCount += naturalBigInt(fragment.costs.unvaluedCallCount, "unvalued call count")
417
+ if (fragment.costs.amount) {
418
+ addAmount(target.amounts, fragment.costs.amount.currency, fragment.costs.amount.amountNanos)
419
+ }
420
+ }
421
+
422
+ function finishAggregateFragment(value: AggregateFragmentAccumulator): AiAccountingAggregate {
423
+ const classifiedCallCount =
424
+ value.ratedCallCount + value.unpriceableCallCount + value.unvaluedCallCount
425
+ if (
426
+ classifiedCallCount !== value.modelCallCount ||
427
+ value.reportedUsageCallCount > value.modelCallCount ||
428
+ AGGREGATE_USAGE_FIELDS.some(
429
+ (field) => value.usage[field].presentCallCount > value.modelCallCount
430
+ )
431
+ ) {
432
+ throw new TypeError("[Sixb] AI accounting SQL aggregate counts are inconsistent.")
433
+ }
434
+ const modelCallCount = safeAggregateNumber(value.modelCallCount, "model-call count")
435
+ const usageInput: Partial<Record<AggregateUsageField, number>> = {}
436
+ if (value.modelCallCount > 0n) {
437
+ for (const field of AGGREGATE_USAGE_FIELDS) {
438
+ const aggregate = value.usage[field]
439
+ if (aggregate.presentCallCount === value.modelCallCount) {
440
+ usageInput[field] = safeAggregateNumber(aggregate.total, field)
441
+ }
442
+ }
443
+ }
444
+ let usage: AiModelCallUsage = normalizeAiModelCallUsage(usageInput)
445
+ if (usage.reportingStatus === "unavailable" && value.reportedUsageCallCount > 0n) {
446
+ usage = { ...usage, reportingStatus: "partial" }
447
+ }
448
+ return {
449
+ modelCallCount,
450
+ usage,
451
+ costs: {
452
+ amounts: [...value.amounts]
453
+ .sort(([left], [right]) => left.localeCompare(right))
454
+ .map(([currency, amountNanos]) => ({ currency, amountNanos: amountNanos.toString() })),
455
+ ratedCallCount: safeAggregateNumber(value.ratedCallCount, "rated call count"),
456
+ unpriceableCallCount: safeAggregateNumber(
457
+ value.unpriceableCallCount,
458
+ "unpriceable call count"
459
+ ),
460
+ unvaluedCallCount: safeAggregateNumber(value.unvaluedCallCount, "unvalued call count"),
461
+ },
462
+ }
463
+ }
464
+
465
+ function naturalBigInt(value: string, field: string): bigint {
466
+ if (!/^(0|[1-9]\d*)$/.test(value)) {
467
+ throw new TypeError(`[Sixb] AI accounting SQL ${field} must be a natural integer.`)
468
+ }
469
+ return BigInt(value)
470
+ }
471
+
472
+ function safeAggregateNumber(value: bigint, field: string): number {
473
+ if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
474
+ throw new TypeError(`[Sixb] AI accounting ${field} exceeds the safe integer range.`)
475
+ }
476
+ return Number(value)
477
+ }
478
+
479
+ function requiredFragmentString(value: string | undefined, field: string): string {
480
+ if (value === undefined || value.length === 0) {
481
+ throw new TypeError(`[Sixb] AI accounting SQL ${field} is missing.`)
482
+ }
483
+ return value
484
+ }
485
+
486
+ function requiredFragmentDate(value: Date | undefined, field: string): Date {
487
+ if (value === undefined) throw new TypeError(`[Sixb] AI accounting SQL ${field} is missing.`)
488
+ assertDate(value, field)
489
+ return value
490
+ }
491
+
492
+ function accountingPeriods(
493
+ from: Date,
494
+ to: Date,
495
+ bucket: AiAccountingBucket
496
+ ): Array<{ start: Date; end: Date }> {
497
+ const result: Array<{ start: Date; end: Date }> = []
498
+ let cursor = aiAccountingBucketStart(from, bucket)
499
+ while (cursor.getTime() < to.getTime()) {
500
+ if (result.length >= MAX_CHART_BUCKETS) {
501
+ throw new RangeError(`[Sixb] AI accounting query exceeds ${MAX_CHART_BUCKETS} chart buckets.`)
502
+ }
503
+ const next = nextBucket(cursor, bucket)
504
+ result.push({ start: new Date(cursor), end: next })
505
+ cursor = next
506
+ }
507
+ return result
508
+ }
509
+
510
+ function nextBucket(start: Date, bucket: AiAccountingBucket): Date {
511
+ const next = new Date(start)
512
+ if (bucket === "hour") next.setUTCHours(next.getUTCHours() + 1)
513
+ else if (bucket === "day") next.setUTCDate(next.getUTCDate() + 1)
514
+ else next.setUTCDate(next.getUTCDate() + 7)
515
+ return next
516
+ }
517
+
518
+ function appendGrouped(
519
+ groups: Map<string, AiAccountingRecordSetItem[]>,
520
+ key: string,
521
+ item: AiAccountingRecordSetItem
522
+ ): void {
523
+ const existing = groups.get(key)
524
+ if (existing) existing.push(item)
525
+ else groups.set(key, [item])
526
+ }
527
+
528
+ function addAmount(amounts: Map<string, bigint>, currency: string, amountNanos: string): void {
529
+ amounts.set(currency, (amounts.get(currency) ?? 0n) + BigInt(amountNanos))
530
+ }
531
+
532
+ function normalizePage(
533
+ requestedLimit: number | undefined,
534
+ requestedOffset: number | undefined
535
+ ): { limit: number; offset: number } {
536
+ const limit = requestedLimit ?? DEFAULT_MODEL_CALL_PAGE_SIZE
537
+ const offset = requestedOffset ?? 0
538
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_MODEL_CALL_PAGE_SIZE) {
539
+ throw new TypeError(
540
+ `[Sixb] AI accounting list limit must be an integer between 1 and ${MAX_MODEL_CALL_PAGE_SIZE}.`
541
+ )
542
+ }
543
+ if (!Number.isSafeInteger(offset) || offset < 0) {
544
+ throw new TypeError("[Sixb] AI accounting list offset must be a non-negative integer.")
545
+ }
546
+ return { limit, offset }
547
+ }
548
+
549
+ function compareAccountingItems(
550
+ left: AiModelCallAccountingItem,
551
+ right: AiModelCallAccountingItem
552
+ ): number {
553
+ return (
554
+ right.usage.occurredAt.getTime() - left.usage.occurredAt.getTime() ||
555
+ left.usage.id.localeCompare(right.usage.id)
556
+ )
557
+ }
558
+
559
+ function compareAgentBreakdowns(
560
+ left: AiAccountingAgentBreakdown,
561
+ right: AiAccountingAgentBreakdown
562
+ ): number {
563
+ return right.modelCallCount - left.modelCallCount || left.agentId.localeCompare(right.agentId)
564
+ }
565
+
566
+ function compareWorkflowBreakdowns(
567
+ left: AiAccountingWorkflowBreakdown,
568
+ right: AiAccountingWorkflowBreakdown
569
+ ): number {
570
+ return (
571
+ right.modelCallCount - left.modelCallCount || left.workflowId.localeCompare(right.workflowId)
572
+ )
573
+ }
574
+
575
+ function compareModelBreakdowns(
576
+ left: AiAccountingModelBreakdown,
577
+ right: AiAccountingModelBreakdown
578
+ ): number {
579
+ return (
580
+ right.modelCallCount - left.modelCallCount ||
581
+ left.providerId.localeCompare(right.providerId) ||
582
+ left.modelId.localeCompare(right.modelId)
583
+ )
584
+ }
585
+
586
+ function assertAiAccountingBucket(bucket: AiAccountingBucket): void {
587
+ if (bucket !== "hour" && bucket !== "day" && bucket !== "week") {
588
+ throw new TypeError("[Sixb] AI accounting bucket must be hour, day, or week.")
589
+ }
590
+ }
591
+
592
+ function assertDate(value: Date, label: string): void {
593
+ if (!(value instanceof Date) || !Number.isFinite(value.getTime())) {
594
+ throw new TypeError(`[Sixb] AI ${label} must be a valid Date.`)
595
+ }
596
+ }
597
+
598
+ function assertNonBlank(value: string, label: string): void {
599
+ if (typeof value !== "string" || value.trim().length === 0) {
600
+ throw new TypeError(`[Sixb] AI ${label} must be nonblank.`)
601
+ }
602
+ }