@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,320 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import type { AiCostStorage, AiModelCallCostRecord } from "../storage/ai-cost"
3
+ import { AiCostStorageError } from "../storage/ai-cost"
4
+ import type { AiUsageStorage } from "../storage/ai-usage"
5
+ import type { ExecutionStorage } from "../storage/executions"
6
+
7
+ export interface AiStorageContractSuiteOptions<TStorage> {
8
+ readonly createStorage: () => TStorage | Promise<TStorage>
9
+ readonly setup?: (storage: TStorage) => void | Promise<void>
10
+ readonly cleanup?: (storage: TStorage) => void | Promise<void>
11
+ }
12
+
13
+ const projectId = "cost-contract-project"
14
+ const executionId = "exec_cost_1"
15
+ const otherExecutionId = "exec_cost_2"
16
+
17
+ function priceSource(entry: string) {
18
+ return {
19
+ sourceId: "test-catalog",
20
+ sourceEntryId: entry,
21
+ sourceVersion: "test-catalog-v1",
22
+ sourceUrl: "https://example.test/ai-pricing.json",
23
+ observedAt: new Date("2026-08-01T00:00:00.000Z"),
24
+ }
25
+ }
26
+
27
+ function ratedCost(
28
+ usageRecordId = "usage_1",
29
+ overrides: Partial<Extract<AiModelCallCostRecord, { status: "rated" }>> = {}
30
+ ): Extract<AiModelCallCostRecord, { status: "rated" }> {
31
+ return {
32
+ projectId,
33
+ usageRecordId,
34
+ status: "rated",
35
+ billingIdentity: { providerId: "vercel", modelId: "openai/gpt-5" },
36
+ pricingContext: {},
37
+ priceSource: priceSource("vercel/openai/gpt-5"),
38
+ money: { currency: "USD", amountNanos: "95000" },
39
+ components: [
40
+ {
41
+ meter: "tokens.input.uncached",
42
+ quantity: "12",
43
+ rateAmountNanosPerMillion: "1250000000",
44
+ chargeAmountNanos: "15000",
45
+ },
46
+ {
47
+ meter: "tokens.input.cacheRead",
48
+ quantity: "0",
49
+ rateAmountNanosPerMillion: "125000000",
50
+ chargeAmountNanos: "0",
51
+ },
52
+ {
53
+ meter: "tokens.output.total",
54
+ quantity: "8",
55
+ rateAmountNanosPerMillion: "10000000000",
56
+ chargeAmountNanos: "80000",
57
+ },
58
+ ],
59
+ ratedAt: new Date("2026-08-01T12:00:00.200Z"),
60
+ ...overrides,
61
+ }
62
+ }
63
+
64
+ function unpriceableCost(): Extract<AiModelCallCostRecord, { status: "unpriceable" }> {
65
+ return {
66
+ projectId,
67
+ usageRecordId: "usage_2",
68
+ status: "unpriceable",
69
+ billingIdentity: { providerId: "vercel", modelId: "unpriced/model" },
70
+ pricingContext: {},
71
+ priceSource: priceSource("vercel/unpriced/model"),
72
+ reason: "missingCatalogEntry",
73
+ ratedAt: new Date("2026-08-01T12:00:00.200Z"),
74
+ }
75
+ }
76
+
77
+ export function runAiCostStorageContractSuite<TStorage extends AiCostStorage>(
78
+ name: string,
79
+ options: AiStorageContractSuiteOptions<TStorage>
80
+ ): void {
81
+ describe(name, () => {
82
+ async function fixture(): Promise<TStorage> {
83
+ const storage = await options.createStorage()
84
+ await options.setup?.(storage)
85
+ return storage
86
+ }
87
+
88
+ test("records model-call costs idempotently", async () => {
89
+ const storage = await fixture()
90
+ try {
91
+ await expect(storage.recordModelCallCost(ratedCost())).resolves.toBeUndefined()
92
+ await expect(storage.recordModelCallCost(ratedCost())).resolves.toBeUndefined()
93
+ await expect(
94
+ storage.listModelCalls({
95
+ projectId,
96
+ from: new Date("2026-08-01T00:00:00.000Z"),
97
+ to: new Date("2026-08-02T00:00:00.000Z"),
98
+ valuationStatus: "rated",
99
+ })
100
+ ).resolves.toMatchObject({
101
+ total: 1,
102
+ items: [{ usage: { id: "usage_1" }, cost: ratedCost(), valuationStatus: "rated" }],
103
+ })
104
+ } finally {
105
+ await options.cleanup?.(storage)
106
+ }
107
+ })
108
+
109
+ test("stores explicit unpriceable results and pricing context", async () => {
110
+ const storage = await fixture()
111
+ try {
112
+ const deploymentCost: AiModelCallCostRecord = {
113
+ projectId,
114
+ usageRecordId: "usage_deployment",
115
+ status: "unpriceable",
116
+ billingIdentity: { providerId: "openai", modelId: "gpt-5" },
117
+ pricingContext: { deploymentId: "production" },
118
+ priceSource: priceSource("openai/gpt-5"),
119
+ reason: "unsupportedPricingDimension",
120
+ ratedAt: new Date("2026-08-03T12:00:00.200Z"),
121
+ }
122
+ await storage.recordModelCallCost(unpriceableCost())
123
+ await storage.recordModelCallCost(deploymentCost)
124
+ await expect(
125
+ storage.listModelCalls({
126
+ projectId,
127
+ from: new Date("2026-08-03T00:00:00.000Z"),
128
+ to: new Date("2026-08-04T00:00:00.000Z"),
129
+ valuationStatus: "unpriceable",
130
+ })
131
+ ).resolves.toMatchObject({
132
+ total: 1,
133
+ items: [{ cost: deploymentCost, valuationStatus: "unpriceable" }],
134
+ })
135
+ } finally {
136
+ await options.cleanup?.(storage)
137
+ }
138
+ })
139
+
140
+ test("summarizes and queries accounting without inventing missing costs", async () => {
141
+ const storage = await fixture()
142
+ try {
143
+ await storage.recordModelCallCost(ratedCost())
144
+ await storage.recordModelCallCost(unpriceableCost())
145
+ await expect(
146
+ storage.summarizeExecutions({ projectId, executionIds: [executionId] })
147
+ ).resolves.toEqual([
148
+ {
149
+ amounts: [{ currency: "USD", amountNanos: "95000" }],
150
+ ratedCallCount: 1,
151
+ unpriceableCallCount: 1,
152
+ unvaluedCallCount: 1,
153
+ },
154
+ ])
155
+
156
+ const overview = await storage.queryProjectOverview({
157
+ projectId,
158
+ from: new Date("2026-08-01T00:00:00.000Z"),
159
+ to: new Date("2026-08-02T00:00:00.000Z"),
160
+ bucket: "day",
161
+ })
162
+ expect(overview.totals).toEqual({
163
+ modelCallCount: 3,
164
+ usage: {
165
+ inputTokens: 15,
166
+ outputTokens: 12,
167
+ totalTokens: 27,
168
+ reportingStatus: "complete",
169
+ },
170
+ costs: {
171
+ amounts: [{ currency: "USD", amountNanos: "95000" }],
172
+ ratedCallCount: 1,
173
+ unpriceableCallCount: 1,
174
+ unvaluedCallCount: 1,
175
+ },
176
+ })
177
+ expect(overview.series).toMatchObject([{ modelCallCount: 3 }])
178
+ expect(overview.models).toMatchObject([
179
+ { providerId: "gateway", modelId: "openai/gpt-5", modelCallCount: 2 },
180
+ { providerId: "gateway", modelId: "unpriced/model", modelCallCount: 1 },
181
+ ])
182
+ await expect(
183
+ storage.queryProjectOverview({
184
+ projectId,
185
+ from: new Date("2026-08-01T00:00:00.000Z"),
186
+ to: new Date("2026-08-02T00:00:00.000Z"),
187
+ bucket: "day",
188
+ providerId: "gateway",
189
+ modelId: "unpriced/model",
190
+ })
191
+ ).resolves.toMatchObject({ totals: { modelCallCount: 1 } })
192
+ await expect(
193
+ storage.listModelCalls({
194
+ projectId,
195
+ from: new Date("2026-08-01T00:00:00.000Z"),
196
+ to: new Date("2026-08-02T00:00:00.000Z"),
197
+ limit: 1,
198
+ offset: 1,
199
+ })
200
+ ).resolves.toMatchObject({
201
+ total: 3,
202
+ hasMore: true,
203
+ items: [{ usage: { id: "usage_2" } }],
204
+ })
205
+ await expect(
206
+ storage.listModelCalls({
207
+ projectId,
208
+ from: new Date("2026-08-01T00:00:00.000Z"),
209
+ to: new Date("2026-08-02T00:00:00.000Z"),
210
+ valuationStatus: "unpriceable",
211
+ })
212
+ ).resolves.toMatchObject({
213
+ total: 1,
214
+ hasMore: false,
215
+ items: [{ usage: { id: "usage_2" }, valuationStatus: "unpriceable" }],
216
+ })
217
+ } finally {
218
+ await options.cleanup?.(storage)
219
+ }
220
+ })
221
+
222
+ test("rejects missing usage and mismatched billed quantities", async () => {
223
+ const storage = await fixture()
224
+ try {
225
+ const missing = ratedCost("missing")
226
+ const missingError = await storage.recordModelCallCost(missing).catch((value) => value)
227
+ expect(missingError).toBeInstanceOf(AiCostStorageError)
228
+ expect((missingError as AiCostStorageError).code).toBe("missing_usage")
229
+
230
+ const base = ratedCost()
231
+ const mismatch = ratedCost("usage_1", {
232
+ money: { currency: "USD", amountNanos: "96250" },
233
+ components: [
234
+ { ...base.components[0]!, quantity: "13", chargeAmountNanos: "16250" },
235
+ ...base.components.slice(1),
236
+ ],
237
+ })
238
+ const mismatchError = await storage.recordModelCallCost(mismatch).catch((value) => value)
239
+ expect(mismatchError).toBeInstanceOf(AiCostStorageError)
240
+ expect((mismatchError as AiCostStorageError).code).toBe("cost_mismatch")
241
+ } finally {
242
+ await options.cleanup?.(storage)
243
+ }
244
+ })
245
+ })
246
+ }
247
+
248
+ /** Seed immutable usage used by every cost-storage provider contract. */
249
+ export async function seedAiCostStorageContractUsage(
250
+ executions: ExecutionStorage,
251
+ usage: AiUsageStorage
252
+ ): Promise<void> {
253
+ for (const fixture of [
254
+ { projectId, executionId },
255
+ { projectId, executionId: otherExecutionId },
256
+ ]) {
257
+ if (await executions.getById({ projectId: fixture.projectId, id: fixture.executionId }))
258
+ continue
259
+ const primitive = {
260
+ kind: "workflow" as const,
261
+ id: "ai-cost-contract",
262
+ runId: fixture.executionId,
263
+ }
264
+ await executions.create({
265
+ id: fixture.executionId,
266
+ projectId: fixture.projectId,
267
+ executor: { type: "primitive", kind: primitive.kind, runId: primitive.runId },
268
+ source: { type: "event", eventId: `test_event:${fixture.executionId}` },
269
+ correlationId: `test_correlation:${fixture.executionId}`,
270
+ authorizationRef: { type: "trustedPrimitive", primitive },
271
+ })
272
+ }
273
+
274
+ for (const fixture of [
275
+ { id: "usage_1", model: "openai/gpt-5", input: 12, output: 8 },
276
+ { id: "usage_2", model: "unpriced/model", input: 1, output: 1 },
277
+ { id: "usage_3", model: "openai/gpt-5", input: 2, output: 3 },
278
+ ]) {
279
+ await usage.recordModelCall({
280
+ id: fixture.id,
281
+ projectId,
282
+ executionId,
283
+ attempt: 1,
284
+ callId: `call_${fixture.id}`,
285
+ requesterGroupIds: [],
286
+ providerId: "gateway",
287
+ requestedModelId: fixture.model,
288
+ responseId: `response_${projectId}_${fixture.id}`,
289
+ usage: {
290
+ inputTokens: fixture.input,
291
+ outputTokens: fixture.output,
292
+ ...(fixture.id === "usage_1"
293
+ ? { uncachedInputTokens: fixture.input, cacheReadInputTokens: 0 }
294
+ : {}),
295
+ },
296
+ occurredAt: new Date("2026-08-01T12:00:00.000Z"),
297
+ recordedAt: new Date("2026-08-01T12:00:00.100Z"),
298
+ })
299
+ }
300
+
301
+ await usage.recordModelCall({
302
+ id: "usage_deployment",
303
+ projectId,
304
+ executionId: otherExecutionId,
305
+ attempt: 1,
306
+ callId: "call_usage_deployment",
307
+ requesterGroupIds: [],
308
+ providerId: "openai.responses",
309
+ requestedModelId: "gpt-5",
310
+ responseId: "response_usage_deployment",
311
+ usage: {
312
+ inputTokens: 2,
313
+ outputTokens: 3,
314
+ uncachedInputTokens: 2,
315
+ cacheReadInputTokens: 0,
316
+ },
317
+ occurredAt: new Date("2026-08-03T12:00:00.000Z"),
318
+ recordedAt: new Date("2026-08-03T12:00:00.100Z"),
319
+ })
320
+ }
@@ -145,6 +145,41 @@ export function runAiUsageStorageContractSuite<TStorage extends AiUsageStorage>(
145
145
  })
146
146
  })
147
147
 
148
+ test("returns the latest completed call for one execution", async () => {
149
+ await withStorage(async (storage) => {
150
+ await expect(
151
+ storage.getLatestForExecution({ projectId, executionId: agentExecutionId })
152
+ ).resolves.toBeNull()
153
+
154
+ await storage.recordModelCall(
155
+ modelCallInput({
156
+ id: "usage_later",
157
+ callId: "call_later",
158
+ responseId: "response_later",
159
+ occurredAt: at("2026-06-23T10:02:00.000Z"),
160
+ })
161
+ )
162
+ await storage.recordModelCall(
163
+ modelCallInput({
164
+ id: "usage_earlier",
165
+ callId: "call_earlier",
166
+ responseId: "response_earlier",
167
+ occurredAt: at("2026-06-23T10:01:00.000Z"),
168
+ })
169
+ )
170
+
171
+ await expect(
172
+ storage.getLatestForExecution({ projectId, executionId: agentExecutionId })
173
+ ).resolves.toMatchObject({ id: "usage_later", responseId: "response_later" })
174
+ await expect(
175
+ storage.getLatestForExecution({
176
+ projectId: "other-project",
177
+ executionId: agentExecutionId,
178
+ })
179
+ ).resolves.toBeNull()
180
+ })
181
+ })
182
+
148
183
  test("bills the same call and response IDs again on a later delivery attempt", async () => {
149
184
  // Regression guard: remove `attempt` from a provider's idempotency key/query/index and the
150
185
  // second append collapses into the first, making the aggregate assertions fail.
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, test } from "bun:test"
2
2
  import { defineObjectType, link, OntologyRegistry, prop } from "../ontology"
3
- import type { Storage } from "../storage"
3
+ import { linkBatchKey, objectBatchKey, objectLinkCursor, type Storage } from "../storage"
4
4
  import { createMaterializerTestFixture } from "./materializer-fixture"
5
5
 
6
6
  export interface EffectiveStorageContractSuiteOptions<TStorage extends Storage> {
@@ -19,7 +19,25 @@ const Device = defineObjectType({
19
19
  ],
20
20
  links: [link.self("peers", { cardinality: "many" })],
21
21
  })
22
- const ontology = new OntologyRegistry({ sources: [Device] })
22
+ const OpaqueA = defineObjectType({
23
+ id: "A",
24
+ name: "Opaque A",
25
+ properties: [
26
+ prop("id", "string", { primary: true, required: true }),
27
+ prop("name", "string", { required: true }),
28
+ ],
29
+ links: [link.self("D", { cardinality: "many" })],
30
+ })
31
+ const OpaqueAB = defineObjectType({
32
+ id: "A:B",
33
+ name: "Opaque AB",
34
+ properties: [
35
+ prop("id", "string", { primary: true, required: true }),
36
+ prop("name", "string", { required: true }),
37
+ ],
38
+ links: [link.self("D", { cardinality: "many" })],
39
+ })
40
+ const ontology = new OntologyRegistry({ sources: [Device, OpaqueA, OpaqueAB] })
23
41
 
24
42
  export function runEffectiveStorageContractSuite<TStorage extends Storage>(
25
43
  name: string,
@@ -56,7 +74,7 @@ export function runEffectiveStorageContractSuite<TStorage extends Storage>(
56
74
  { objectTypeId: Device.id, primaryId: "missing" },
57
75
  ],
58
76
  })
59
- expect([...batch.keys()]).toEqual([`${Device.id}:a`])
77
+ expect([...batch.keys()]).toEqual([objectBatchKey(Device.id, "a")])
60
78
  })
61
79
  })
62
80
 
@@ -81,7 +99,179 @@ export function runEffectiveStorageContractSuite<TStorage extends Storage>(
81
99
  projectId,
82
100
  items: [{ objectTypeId: Device.id, objectId: "a", linkId: "peers" }],
83
101
  })
84
- expect(linksByScope.get(`${Device.id}:a:peers`)).toHaveLength(2)
102
+ expect(linksByScope.get(linkBatchKey(Device.id, "a", "peers"))).toHaveLength(2)
103
+ })
104
+ })
105
+
106
+ test("pages and filters incident links in the provider", async () => {
107
+ await withStorage(async (storage) => {
108
+ const first = await storage.objects.queryLinks({
109
+ projectId,
110
+ objectRefs: [{ objectTypeId: Device.id, primaryId: "a" }],
111
+ direction: "outgoing",
112
+ linkId: "peers",
113
+ endpointObjectTypeIds: [Device.id],
114
+ limit: 1,
115
+ })
116
+ expect(first.links.map((row) => row.targetId)).toEqual(["b"])
117
+ expect(first.hasMore).toBe(true)
118
+
119
+ const second = await storage.objects.queryLinks({
120
+ projectId,
121
+ objectRefs: [{ objectTypeId: Device.id, primaryId: "a" }],
122
+ direction: "outgoing",
123
+ linkId: "peers",
124
+ endpointObjectTypeIds: [Device.id],
125
+ after: objectLinkCursor(first.links[0]),
126
+ limit: 1,
127
+ })
128
+ expect(second.links.map((row) => row.targetId)).toEqual(["c"])
129
+ expect(second.hasMore).toBe(false)
130
+
131
+ const duplicateRefs = await storage.objects.queryLinks({
132
+ projectId,
133
+ objectRefs: [
134
+ { objectTypeId: Device.id, primaryId: "a" },
135
+ { objectTypeId: Device.id, primaryId: "a" },
136
+ ],
137
+ direction: "outgoing",
138
+ limit: 10,
139
+ })
140
+ expect(duplicateRefs.links.map((row) => row.targetId)).toEqual(["b", "c"])
141
+
142
+ const incoming = await storage.objects.queryLinks({
143
+ projectId,
144
+ objectRefs: [{ objectTypeId: Device.id, primaryId: "b" }],
145
+ direction: "incoming",
146
+ limit: 10,
147
+ })
148
+ expect(incoming.links.map((row) => [row.sourceId, row.targetId])).toEqual([["a", "b"]])
149
+
150
+ const bothDirections = await storage.objects.queryLinks({
151
+ projectId,
152
+ objectRefs: [
153
+ { objectTypeId: Device.id, primaryId: "a" },
154
+ { objectTypeId: Device.id, primaryId: "b" },
155
+ ],
156
+ direction: "both",
157
+ endpointObjectTypeIds: [Device.id],
158
+ limit: 10,
159
+ })
160
+ expect(bothDirections.links.map((row) => [row.sourceId, row.targetId])).toEqual([
161
+ ["a", "b"],
162
+ ["a", "c"],
163
+ ])
164
+
165
+ const unauthorized = await storage.objects.queryLinks({
166
+ projectId,
167
+ objectRefs: [{ objectTypeId: Device.id, primaryId: "a" }],
168
+ direction: "both",
169
+ endpointObjectTypeIds: [],
170
+ limit: 10,
171
+ })
172
+ expect(unauthorized).toEqual({ links: [], hasMore: false })
173
+ })
174
+ })
175
+
176
+ test("keeps delimiter-bearing object and batch-link identities distinct", async () => {
177
+ await withStorage(async (storage) => {
178
+ const opaqueProjectId = `${projectId}-opaque-batch-keys`
179
+ const fixture = createMaterializerTestFixture({
180
+ projectId: opaqueProjectId,
181
+ ontology,
182
+ storage,
183
+ })
184
+ await fixture.seed({
185
+ objects: [
186
+ {
187
+ ref: { objectTypeId: OpaqueA.id, primaryId: "B:C" },
188
+ properties: { id: "B:C", name: "First" },
189
+ },
190
+ {
191
+ ref: { objectTypeId: OpaqueAB.id, primaryId: "C" },
192
+ properties: { id: "C", name: "Second" },
193
+ },
194
+ ],
195
+ links: [
196
+ {
197
+ ref: {
198
+ source: { objectTypeId: OpaqueA.id, primaryId: "B:C" },
199
+ linkId: "D",
200
+ target: { objectTypeId: OpaqueA.id, primaryId: "B:C" },
201
+ },
202
+ },
203
+ {
204
+ ref: {
205
+ source: { objectTypeId: OpaqueAB.id, primaryId: "C" },
206
+ linkId: "D",
207
+ target: { objectTypeId: OpaqueAB.id, primaryId: "C" },
208
+ },
209
+ },
210
+ ],
211
+ })
212
+
213
+ const objects = await storage.objects.getByPrimaryIdBatch({
214
+ projectId: opaqueProjectId,
215
+ items: [
216
+ { objectTypeId: OpaqueA.id, primaryId: "B:C" },
217
+ { objectTypeId: OpaqueAB.id, primaryId: "C" },
218
+ ],
219
+ })
220
+ expect(objects.get(objectBatchKey(OpaqueA.id, "B:C"))?.properties.name).toBe("First")
221
+ expect(objects.get(objectBatchKey(OpaqueAB.id, "C"))?.properties.name).toBe("Second")
222
+
223
+ const links = await storage.objects.listLinksBatch({
224
+ projectId: opaqueProjectId,
225
+ items: [
226
+ { objectTypeId: OpaqueA.id, objectId: "B:C", linkId: "D" },
227
+ { objectTypeId: OpaqueAB.id, objectId: "C", linkId: "D" },
228
+ ],
229
+ })
230
+ expect(links.get(linkBatchKey(OpaqueA.id, "B:C", "D"))).toHaveLength(1)
231
+ expect(links.get(linkBatchKey(OpaqueAB.id, "C", "D"))).toHaveLength(1)
232
+ })
233
+ })
234
+
235
+ test("preserves distinct incident links when opaque ids contain delimiters", async () => {
236
+ await withStorage(async (storage) => {
237
+ const opaqueIdsProjectId = `${projectId}-opaque-link-ids`
238
+ const firstSourceId = "opaque:source"
239
+ const firstTargetId = `target:peers:${Device.id}:opaque:end`
240
+ const secondSourceId = `${firstSourceId}:peers:${Device.id}:target`
241
+ const secondTargetId = "opaque:end"
242
+ const fixture = createMaterializerTestFixture({
243
+ projectId: opaqueIdsProjectId,
244
+ ontology,
245
+ storage,
246
+ })
247
+ await fixture.seed({
248
+ objects: [
249
+ device(firstSourceId, "First source"),
250
+ device(firstTargetId, "First target"),
251
+ device(secondSourceId, "Second source"),
252
+ device(secondTargetId, "Second target"),
253
+ ],
254
+ links: [peer(firstSourceId, firstTargetId), peer(secondSourceId, secondTargetId)],
255
+ })
256
+
257
+ const incident = await storage.objects.listIncidentLinksBatch({
258
+ projectId: opaqueIdsProjectId,
259
+ items: [
260
+ { objectTypeId: Device.id, objectId: firstSourceId },
261
+ { objectTypeId: Device.id, objectId: secondSourceId },
262
+ ],
263
+ })
264
+ const matching = incident.filter(
265
+ (row) => row.sourceId === firstSourceId || row.sourceId === secondSourceId
266
+ )
267
+
268
+ expect(matching.map((row) => [row.sourceId, row.linkId, row.targetId])).toEqual(
269
+ expect.arrayContaining([
270
+ [firstSourceId, "peers", firstTargetId],
271
+ [secondSourceId, "peers", secondTargetId],
272
+ ])
273
+ )
274
+ expect(matching).toHaveLength(2)
85
275
  })
86
276
  })
87
277
 
@@ -7,6 +7,11 @@ export {
7
7
  type AgentStorageContractSuiteOptions,
8
8
  runAgentStorageContractSuite,
9
9
  } from "./agent-storage-contract"
10
+ export {
11
+ type AiStorageContractSuiteOptions,
12
+ runAiCostStorageContractSuite,
13
+ seedAiCostStorageContractUsage,
14
+ } from "./ai-cost-storage-contract"
10
15
  export {
11
16
  type AiUsageStorageContractSuiteOptions,
12
17
  runAiUsageStorageContractSuite,
@@ -438,6 +438,62 @@ export function runLakeStorageContractSuite<TStorage extends LakeStorage>(
438
438
  })
439
439
  })
440
440
 
441
+ test("creates an initial empty snapshot version", async () => {
442
+ await withStorage(async (storage) => {
443
+ await storage.createDataset(writeDataset)
444
+
445
+ const write = await storage.beginWrite({ dataset: writeDataset, mode: "snapshot" })
446
+ const version = await write.commit()
447
+
448
+ expect(version).toMatchObject({
449
+ datasetId: writeDataset.id,
450
+ mode: "snapshot",
451
+ outcome: "created",
452
+ rowCount: 0,
453
+ })
454
+ expect(await storage.getLatestVersion(writeDataset.id)).toMatchObject({
455
+ versionId: version.versionId,
456
+ })
457
+ expect(await storage.listVersions(writeDataset.id)).toHaveLength(1)
458
+
459
+ const repeatedWrite = await storage.beginWrite({
460
+ dataset: writeDataset,
461
+ mode: "snapshot",
462
+ })
463
+ const repeatedVersion = await repeatedWrite.commit()
464
+ expect(repeatedVersion.outcome).toBe("unchanged")
465
+ expect(repeatedVersion.versionId).toBe(version.versionId)
466
+ expect(await storage.listVersions(writeDataset.id)).toHaveLength(1)
467
+
468
+ await expect(
469
+ collectRows(storage.readRows({ datasetId: writeDataset.id }))
470
+ ).resolves.toEqual([])
471
+ })
472
+ })
473
+
474
+ test("creates an initial empty append version", async () => {
475
+ await withStorage(async (storage) => {
476
+ await storage.createDataset(writeDataset)
477
+
478
+ const write = await storage.beginWrite({ dataset: writeDataset, mode: "append" })
479
+ const version = await write.commit()
480
+
481
+ expect(version).toMatchObject({
482
+ datasetId: writeDataset.id,
483
+ mode: "append",
484
+ outcome: "created",
485
+ })
486
+ expect(version.parentVersionId).toBeUndefined()
487
+ expect(await storage.getLatestVersion(writeDataset.id)).toMatchObject({
488
+ versionId: version.versionId,
489
+ })
490
+ expect(await storage.listVersions(writeDataset.id)).toHaveLength(1)
491
+ await expect(
492
+ collectRows(storage.readRows({ datasetId: writeDataset.id }))
493
+ ).resolves.toEqual([])
494
+ })
495
+ })
496
+
441
497
  test("snapshot writes replace the visible latest rows", async () => {
442
498
  await withStorage(async (storage) => {
443
499
  await storage.createDataset(writeDataset)