@sixb/core 0.1.3 → 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 (252) hide show
  1. package/dist/actions/index.js +17 -18
  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/context-compaction.d.ts +40 -0
  6. package/dist/agents/context-compaction.d.ts.map +1 -0
  7. package/dist/agents/ids.d.ts +2 -0
  8. package/dist/agents/ids.d.ts.map +1 -1
  9. package/dist/agents/index.d.ts +6 -5
  10. package/dist/agents/index.d.ts.map +1 -1
  11. package/dist/agents/index.js +42 -37
  12. package/dist/agents/index.js.map +1 -1
  13. package/dist/agents/streams.d.ts +17 -1
  14. package/dist/agents/streams.d.ts.map +1 -1
  15. package/dist/agents/streams.js +4 -2
  16. package/dist/agents/streams.js.map +1 -1
  17. package/dist/agents/types.d.ts +9 -0
  18. package/dist/agents/types.d.ts.map +1 -1
  19. package/dist/agents/validation.d.ts +1 -1
  20. package/dist/agents/validation.d.ts.map +1 -1
  21. package/dist/auth/index.js +7 -3
  22. package/dist/auth/index.js.map +1 -1
  23. package/dist/authorization/index.js +6 -2
  24. package/dist/authorization/index.js.map +1 -1
  25. package/dist/bootstrap/index.js +30 -31
  26. package/dist/bootstrap/index.js.map +1 -1
  27. package/dist/{chunk-xmggaxg4.js → chunk-1wk20wn3.js} +188 -21
  28. package/dist/{chunk-xmggaxg4.js.map → chunk-1wk20wn3.js.map} +7 -5
  29. package/dist/{chunk-1ywq4hzk.js → chunk-1yz3x65y.js} +7 -7
  30. package/dist/{chunk-gqdd7508.js → chunk-4rh2m6zg.js} +1 -1
  31. package/dist/{chunk-fmx1sz2h.js → chunk-72q2y8hn.js} +3 -3
  32. package/dist/{chunk-fmx1sz2h.js.map → chunk-72q2y8hn.js.map} +3 -3
  33. package/dist/{chunk-f1cyvnrk.js → chunk-7gw7mepb.js} +259 -9
  34. package/dist/chunk-7gw7mepb.js.map +14 -0
  35. package/dist/{chunk-wb4051cx.js → chunk-819wnjz8.js} +11 -5
  36. package/dist/chunk-819wnjz8.js.map +12 -0
  37. package/dist/{chunk-pxtrxm3p.js → chunk-a09jx26t.js} +1 -1
  38. package/dist/{chunk-ettb0cy4.js → chunk-afjpa2hs.js} +59 -29
  39. package/dist/{chunk-ettb0cy4.js.map → chunk-afjpa2hs.js.map} +5 -5
  40. package/dist/{chunk-177ed2mp.js → chunk-c43qr6s0.js} +229 -97
  41. package/dist/{chunk-177ed2mp.js.map → chunk-c43qr6s0.js.map} +9 -9
  42. package/dist/{chunk-261ag4t4.js → chunk-cbkm2vam.js} +3 -3
  43. package/dist/{chunk-zpw39qhj.js → chunk-dm72y8wt.js} +25 -2
  44. package/dist/chunk-dm72y8wt.js.map +10 -0
  45. package/dist/{chunk-kqxgf6kv.js → chunk-drjqm1aa.js} +16 -1
  46. package/dist/{chunk-kqxgf6kv.js.map → chunk-drjqm1aa.js.map} +3 -3
  47. package/dist/chunk-dysw7nsm.js.map +1 -1
  48. package/dist/{chunk-4aw0jwk0.js → chunk-f6erf2vv.js} +1 -1
  49. package/dist/{chunk-4z3n1dzt.js → chunk-f83ka50g.js} +131 -25
  50. package/dist/chunk-f83ka50g.js.map +23 -0
  51. package/dist/{chunk-km4rkj4t.js → chunk-g13869yf.js} +3 -3
  52. package/dist/{chunk-c8bw2b6c.js → chunk-hbeh8j9w.js} +2 -2
  53. package/dist/{chunk-n5x5y80a.js → chunk-nzw7sm3b.js} +2 -2
  54. package/dist/{chunk-3rsf30p4.js → chunk-pn8n8sba.js} +16 -15
  55. package/dist/{chunk-3rsf30p4.js.map → chunk-pn8n8sba.js.map} +3 -3
  56. package/dist/{chunk-63te770v.js → chunk-qewtks41.js} +8 -8
  57. package/dist/{chunk-63te770v.js.map → chunk-qewtks41.js.map} +2 -2
  58. package/dist/{chunk-4p9rh7jj.js → chunk-rs1wbvy6.js} +1 -1
  59. package/dist/{chunk-88ppkr54.js → chunk-rte5sm3z.js} +26 -2
  60. package/dist/chunk-rte5sm3z.js.map +12 -0
  61. package/dist/{chunk-bv41fmjn.js → chunk-s31d0qmq.js} +57 -20
  62. package/dist/{chunk-bv41fmjn.js.map → chunk-s31d0qmq.js.map} +8 -7
  63. package/dist/{chunk-c6cfqydk.js → chunk-sxwk41qa.js} +1 -1
  64. package/dist/chunk-t0jjkep3.js.map +1 -1
  65. package/dist/{chunk-d6y2wsfg.js → chunk-tek1qwgm.js} +6 -6
  66. package/dist/{chunk-d6y2wsfg.js.map → chunk-tek1qwgm.js.map} +1 -1
  67. package/dist/{chunk-d2adj15j.js → chunk-tkdscva6.js} +1 -1
  68. package/dist/{chunk-ttba8x6z.js → chunk-tycbzedm.js} +2 -2
  69. package/dist/{chunk-z8j83gzd.js → chunk-x2yw73hy.js} +1 -1
  70. package/dist/{chunk-qer6am8w.js → chunk-xdf9yswn.js} +1 -1
  71. package/dist/execution/agent.js +26 -27
  72. package/dist/execution/agent.js.map +1 -1
  73. package/dist/execution/primitive.js +26 -27
  74. package/dist/execution/primitive.js.map +2 -2
  75. package/dist/execution/request.js +27 -28
  76. package/dist/execution/request.js.map +2 -2
  77. package/dist/http/index.js +1 -1
  78. package/dist/index.d.ts +5 -4
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +52 -53
  81. package/dist/index.js.map +1 -1
  82. package/dist/lake-storage/types.d.ts +2 -1
  83. package/dist/lake-storage/types.d.ts.map +1 -1
  84. package/dist/materializer/index.js +15 -16
  85. package/dist/materializer/index.js.map +1 -1
  86. package/dist/models/index.d.ts +40 -0
  87. package/dist/models/index.d.ts.map +1 -0
  88. package/dist/objects/execution.d.ts +6 -1
  89. package/dist/objects/execution.d.ts.map +1 -1
  90. package/dist/objects/index.js +19 -20
  91. package/dist/objects/index.js.map +1 -1
  92. package/dist/objects/link/endpoints.d.ts.map +1 -1
  93. package/dist/objects/query/executor.d.ts +1 -0
  94. package/dist/objects/query/executor.d.ts.map +1 -1
  95. package/dist/objects/query/explain.d.ts +1 -0
  96. package/dist/objects/query/explain.d.ts.map +1 -1
  97. package/dist/objects/query/index.d.ts +3 -1
  98. package/dist/objects/query/index.d.ts.map +1 -1
  99. package/dist/objects/query/index.js +30 -5
  100. package/dist/objects/query/index.js.map +1 -1
  101. package/dist/objects/query/ir.d.ts +14 -1
  102. package/dist/objects/query/ir.d.ts.map +1 -1
  103. package/dist/objects/query/links.d.ts +25 -0
  104. package/dist/objects/query/links.d.ts.map +1 -0
  105. package/dist/objects/query/normalize.d.ts.map +1 -1
  106. package/dist/objects/query/planner.d.ts.map +1 -1
  107. package/dist/objects/query/validate.d.ts +1 -0
  108. package/dist/objects/query/validate.d.ts.map +1 -1
  109. package/dist/pipelines/index.js +15 -16
  110. package/dist/pipelines/index.js.map +1 -1
  111. package/dist/projections/builders.d.ts +33 -4
  112. package/dist/projections/builders.d.ts.map +1 -1
  113. package/dist/projections/index.d.ts +1 -1
  114. package/dist/projections/index.d.ts.map +1 -1
  115. package/dist/projections/internal.js +15 -16
  116. package/dist/projections/internal.js.map +1 -1
  117. package/dist/projections/run-dispatch.d.ts +1 -0
  118. package/dist/projections/run-dispatch.d.ts.map +1 -1
  119. package/dist/projections/types.d.ts +12 -9
  120. package/dist/projections/types.d.ts.map +1 -1
  121. package/dist/projections/validation.d.ts +6 -5
  122. package/dist/projections/validation.d.ts.map +1 -1
  123. package/dist/query.d.ts +2 -1
  124. package/dist/query.d.ts.map +1 -1
  125. package/dist/query.js +2 -2
  126. package/dist/runtime/create.d.ts +3 -0
  127. package/dist/runtime/create.d.ts.map +1 -1
  128. package/dist/runtime/definitions.d.ts +3 -0
  129. package/dist/runtime/definitions.d.ts.map +1 -1
  130. package/dist/runtime/host.d.ts +2 -0
  131. package/dist/runtime/host.d.ts.map +1 -1
  132. package/dist/runtime/resolve-definitions.d.ts +2 -0
  133. package/dist/runtime/resolve-definitions.d.ts.map +1 -1
  134. package/dist/storage/agents/in-memory.d.ts +4 -0
  135. package/dist/storage/agents/in-memory.d.ts.map +1 -1
  136. package/dist/storage/agents/provider.js +9 -5
  137. package/dist/storage/agents/provider.js.map +1 -1
  138. package/dist/storage/agents/types.d.ts +5 -0
  139. package/dist/storage/agents/types.d.ts.map +1 -1
  140. package/dist/storage/ai-cost/provider.js +1 -1
  141. package/dist/storage/ai-usage/in-memory.d.ts +2 -1
  142. package/dist/storage/ai-usage/in-memory.d.ts.map +1 -1
  143. package/dist/storage/ai-usage/index.d.ts +1 -1
  144. package/dist/storage/ai-usage/index.d.ts.map +1 -1
  145. package/dist/storage/ai-usage/types.d.ts +6 -0
  146. package/dist/storage/ai-usage/types.d.ts.map +1 -1
  147. package/dist/storage/index.d.ts +3 -2
  148. package/dist/storage/index.d.ts.map +1 -1
  149. package/dist/storage/index.js +19 -14
  150. package/dist/storage/index.js.map +1 -1
  151. package/dist/storage/objects/in-memory.d.ts +6 -3
  152. package/dist/storage/objects/in-memory.d.ts.map +1 -1
  153. package/dist/storage/objects/index.d.ts +3 -1
  154. package/dist/storage/objects/index.d.ts.map +1 -1
  155. package/dist/storage/objects/keys.d.ts +16 -0
  156. package/dist/storage/objects/keys.d.ts.map +1 -0
  157. package/dist/storage/objects/types.d.ts +33 -6
  158. package/dist/storage/objects/types.d.ts.map +1 -1
  159. package/dist/storage/ontology/provider.js +1 -1
  160. package/dist/storage/workflow-runs/provider.js +9 -5
  161. package/dist/storage/workflow-runs/provider.js.map +1 -1
  162. package/dist/syncs/index.js +15 -16
  163. package/dist/syncs/index.js.map +1 -1
  164. package/dist/testing/agent-storage-contract.d.ts.map +1 -1
  165. package/dist/testing/ai-usage-storage-contract.d.ts.map +1 -1
  166. package/dist/testing/effective-storage-contract.d.ts +1 -1
  167. package/dist/testing/effective-storage-contract.d.ts.map +1 -1
  168. package/dist/testing/index.js +455 -40
  169. package/dist/testing/index.js.map +7 -7
  170. package/dist/testing/lake-storage-contract.d.ts.map +1 -1
  171. package/dist/testing/object-query-contract.d.ts +1 -1
  172. package/dist/testing/object-query-contract.d.ts.map +1 -1
  173. package/dist/webhooks/internal.js +13 -14
  174. package/dist/webhooks/internal.js.map +2 -2
  175. package/dist/workflows/index.js +27 -28
  176. package/dist/workflows/index.js.map +1 -1
  177. package/package.json +1 -1
  178. package/src/agents/builders.ts +3 -3
  179. package/src/agents/context-compaction.ts +261 -0
  180. package/src/agents/ids.ts +5 -0
  181. package/src/agents/index.ts +18 -9
  182. package/src/agents/streams.ts +56 -0
  183. package/src/agents/types.ts +10 -0
  184. package/src/agents/validation.ts +35 -5
  185. package/src/http/api-routes.ts +7 -7
  186. package/src/index.ts +14 -0
  187. package/src/lake-storage/types.ts +2 -1
  188. package/src/materializer/telemetry/append.ts +33 -16
  189. package/src/models/index.ts +71 -0
  190. package/src/objects/execution.ts +20 -0
  191. package/src/objects/link/endpoints.ts +2 -1
  192. package/src/objects/query/executor.ts +42 -11
  193. package/src/objects/query/explain.ts +11 -0
  194. package/src/objects/query/index.ts +6 -0
  195. package/src/objects/query/ir.ts +16 -0
  196. package/src/objects/query/links.ts +308 -0
  197. package/src/objects/query/normalize.ts +40 -1
  198. package/src/objects/query/planner.ts +5 -0
  199. package/src/objects/query/validate.ts +58 -2
  200. package/src/projections/builders.ts +220 -15
  201. package/src/projections/index.ts +1 -0
  202. package/src/projections/ownership.ts +5 -5
  203. package/src/projections/registry.ts +9 -3
  204. package/src/projections/revision.ts +31 -5
  205. package/src/projections/run-dispatch.ts +10 -1
  206. package/src/projections/types.ts +13 -9
  207. package/src/projections/validation.ts +61 -19
  208. package/src/query.ts +2 -0
  209. package/src/runtime/create.ts +4 -0
  210. package/src/runtime/definitions.ts +3 -0
  211. package/src/runtime/host.ts +2 -0
  212. package/src/runtime/resolve-definitions.ts +28 -0
  213. package/src/storage/agents/in-memory.ts +15 -0
  214. package/src/storage/agents/types.ts +5 -0
  215. package/src/storage/ai-usage/in-memory.ts +23 -0
  216. package/src/storage/ai-usage/index.ts +1 -0
  217. package/src/storage/ai-usage/types.ts +10 -0
  218. package/src/storage/index.ts +11 -0
  219. package/src/storage/objects/in-memory.ts +103 -16
  220. package/src/storage/objects/index.ts +9 -0
  221. package/src/storage/objects/keys.ts +36 -0
  222. package/src/storage/objects/types.ts +34 -6
  223. package/src/storage/ontology/provider-header-validation.ts +2 -2
  224. package/src/testing/agent-storage-contract.ts +12 -0
  225. package/src/testing/ai-usage-storage-contract.ts +35 -0
  226. package/src/testing/effective-storage-contract.ts +194 -4
  227. package/src/testing/lake-storage-contract.ts +56 -0
  228. package/src/testing/object-query-contract.ts +208 -1
  229. package/dist/agents/prompt.d.ts +0 -21
  230. package/dist/agents/prompt.d.ts.map +0 -1
  231. package/dist/chunk-4z3n1dzt.js.map +0 -23
  232. package/dist/chunk-6wetht33.js +0 -89
  233. package/dist/chunk-6wetht33.js.map +0 -10
  234. package/dist/chunk-88ppkr54.js.map +0 -12
  235. package/dist/chunk-f1cyvnrk.js.map +0 -13
  236. package/dist/chunk-wb4051cx.js.map +0 -12
  237. package/dist/chunk-zpw39qhj.js.map +0 -10
  238. package/src/agents/prompt.ts +0 -85
  239. /package/dist/{chunk-1ywq4hzk.js.map → chunk-1yz3x65y.js.map} +0 -0
  240. /package/dist/{chunk-gqdd7508.js.map → chunk-4rh2m6zg.js.map} +0 -0
  241. /package/dist/{chunk-pxtrxm3p.js.map → chunk-a09jx26t.js.map} +0 -0
  242. /package/dist/{chunk-261ag4t4.js.map → chunk-cbkm2vam.js.map} +0 -0
  243. /package/dist/{chunk-4aw0jwk0.js.map → chunk-f6erf2vv.js.map} +0 -0
  244. /package/dist/{chunk-km4rkj4t.js.map → chunk-g13869yf.js.map} +0 -0
  245. /package/dist/{chunk-c8bw2b6c.js.map → chunk-hbeh8j9w.js.map} +0 -0
  246. /package/dist/{chunk-n5x5y80a.js.map → chunk-nzw7sm3b.js.map} +0 -0
  247. /package/dist/{chunk-4p9rh7jj.js.map → chunk-rs1wbvy6.js.map} +0 -0
  248. /package/dist/{chunk-c6cfqydk.js.map → chunk-sxwk41qa.js.map} +0 -0
  249. /package/dist/{chunk-d2adj15j.js.map → chunk-tkdscva6.js.map} +0 -0
  250. /package/dist/{chunk-ttba8x6z.js.map → chunk-tycbzedm.js.map} +0 -0
  251. /package/dist/{chunk-z8j83gzd.js.map → chunk-x2yw73hy.js.map} +0 -0
  252. /package/dist/{chunk-qer6am8w.js.map → chunk-xdf9yswn.js.map} +0 -0
@@ -2,6 +2,7 @@ import type { ActionDefinitionCatalog } from "../actions"
2
2
  import type { AgentDefinition } from "../agents"
3
3
  import type { ConnectorDefinition } from "../connectors"
4
4
  import type { DatasetDefinition } from "../datasets"
5
+ import type { ModelCatalog } from "../models"
5
6
  import type { OntologyDefinitionCatalog } from "../ontology"
6
7
  import type { PipelineDefinition } from "../pipelines"
7
8
  import type { ProjectionDefinitionCatalog } from "../projections"
@@ -24,6 +25,8 @@ export interface SixbDefinitions {
24
25
  readonly agents: DefinitionCatalog<AgentDefinition>
25
26
  readonly connectors: DefinitionCatalog<ConnectorDefinition>
26
27
  readonly datasets: DefinitionCatalog<DatasetDefinition>
28
+ /** Absent until a project configures `models` in createSixb(). */
29
+ readonly models?: ModelCatalog
27
30
  readonly pipelines: DefinitionCatalog<PipelineDefinition>
28
31
  readonly projections: ProjectionDefinitionCatalog
29
32
  readonly rules: DefinitionCatalog<RuleDefinition>
@@ -48,6 +48,7 @@ import {
48
48
  type SixbReadiness,
49
49
  } from "../maintenance"
50
50
  import { createOntologyMaterializer, type OntologyMaterializerContract } from "../materializer"
51
+ import type { ModelCatalogInput } from "../models"
51
52
  import type { PipelineDefinition } from "../pipelines/types"
52
53
  import { registerProjectionRegistry } from "../projections/internal"
53
54
  import type { ProjectionDefinition } from "../projections/types"
@@ -108,6 +109,7 @@ export interface SixbHostOptions<TOntologySources extends readonly OntologySourc
108
109
  rules?: readonly RuleDefinition[]
109
110
  workflows?: readonly WorkflowDefinition[]
110
111
  agents?: readonly AgentDefinition[]
112
+ models?: ModelCatalogInput
111
113
  groups?: readonly GroupDefinition[]
112
114
  roles?: readonly RoleDefinition[]
113
115
  membershipPolicies?: readonly MembershipPolicyDefinition[]
@@ -5,6 +5,7 @@ import { validateAgentGroupReferences, validateAgentToolsAtStartup } from "../ag
5
5
  import type { ConnectorDefinition } from "../connectors"
6
6
  import type { DatasetDefinition } from "../datasets/types"
7
7
  import { assertDatasetDefinition } from "../datasets/validation"
8
+ import { createModelCatalog, type ModelCatalog, type ModelCatalogInput, modelRef } from "../models"
8
9
  import { OntologyRegistry } from "../ontology"
9
10
  import type { PipelineDefinition } from "../pipelines/types"
10
11
  import { ProjectionRegistry } from "../projections"
@@ -40,6 +41,7 @@ interface DefinitionOptions {
40
41
  readonly rules?: readonly RuleDefinition[]
41
42
  readonly workflows?: readonly WorkflowDefinition[]
42
43
  readonly agents?: readonly AgentDefinition[]
44
+ readonly models?: ModelCatalogInput
43
45
  readonly groups?: readonly GroupDefinition[]
44
46
  readonly roles?: readonly RoleDefinition[]
45
47
  readonly membershipPolicies?: readonly MembershipPolicyDefinition[]
@@ -60,6 +62,8 @@ export function resolveDefinitions(options: DefinitionOptions): ResolvedDefiniti
60
62
 
61
63
  const agents = options.agents ?? []
62
64
  validateAgentToolsAtStartup(agents)
65
+ const models = options.models === undefined ? undefined : createModelCatalog(options.models)
66
+ validateAgentModelReferences(agents, models)
63
67
  const agentsById = indexUniqueDefinitions("agent", agents)
64
68
  const connectorsById = indexUniqueDefinitions("connector", options.connectors ?? [])
65
69
 
@@ -158,6 +162,7 @@ export function resolveDefinitions(options: DefinitionOptions): ResolvedDefiniti
158
162
  agents: createDefinitionCatalog(agentsById),
159
163
  connectors: createDefinitionCatalog(connectorsById),
160
164
  datasets: createDefinitionCatalog(datasetsById),
165
+ ...(models === undefined ? {} : { models }),
161
166
  pipelines: createDefinitionCatalog(pipelinesById),
162
167
  projections: projectionRegistry,
163
168
  rules: createDefinitionCatalog(rulesById),
@@ -236,3 +241,26 @@ function validateScheduleReferences(
236
241
  }
237
242
  }
238
243
  }
244
+
245
+ /**
246
+ * Keep every agent on a model the project actually allows.
247
+ *
248
+ * Only enforced once a project configures `models`; without a catalog an agent's model is its own
249
+ * declaration, which is the pre-catalog behavior.
250
+ */
251
+ function validateAgentModelReferences(
252
+ agents: readonly AgentDefinition[],
253
+ models: ModelCatalog | undefined
254
+ ): void {
255
+ if (models === undefined) {
256
+ return
257
+ }
258
+ for (const agent of agents) {
259
+ const ref = modelRef(agent.model)
260
+ if (models.language.getById(ref) === null) {
261
+ throw new RuntimeError(
262
+ `[Sixb] Agent '${agent.id}' uses unknown language model '${ref}'. Add it to 'models.language' in createSixb().`
263
+ )
264
+ }
265
+ }
266
+ }
@@ -642,6 +642,21 @@ class InMemoryAgentContextCheckpointStore implements AgentContextCheckpointStore
642
642
  return record ? clone(record) : null
643
643
  }
644
644
 
645
+ async getByRunIds(input: {
646
+ readonly projectId: string
647
+ readonly runIds: readonly string[]
648
+ }): Promise<readonly AgentContextCheckpointRecord[]> {
649
+ const byRunId = new Map(
650
+ [...this.state.checkpoints.values()]
651
+ .filter((checkpoint) => checkpoint.projectId === input.projectId)
652
+ .map((checkpoint) => [checkpoint.createdByRunId, checkpoint] as const)
653
+ )
654
+ return [...new Set(input.runIds)].flatMap((runId) => {
655
+ const checkpoint = byRunId.get(runId)
656
+ return checkpoint ? [clone(checkpoint)] : []
657
+ })
658
+ }
659
+
645
660
  private findLatest(projectId: string, threadId: string): AgentContextCheckpointRecord | null {
646
661
  return (
647
662
  [...this.state.checkpoints.values()]
@@ -408,6 +408,11 @@ export interface AgentContextCheckpointStore {
408
408
  * Repeating the same payload for the same creating run is idempotent.
409
409
  */
410
410
  create(input: CreateAgentContextCheckpointInput): Promise<AgentContextCheckpointRecord>
411
+ /** Return checkpoints created by the requested runs, in first-occurrence input order. */
412
+ getByRunIds(input: {
413
+ readonly projectId: string
414
+ readonly runIds: readonly string[]
415
+ }): Promise<readonly AgentContextCheckpointRecord[]>
411
416
  getLatest(input: {
412
417
  readonly projectId: string
413
418
  readonly threadId: string
@@ -5,6 +5,7 @@ import type {
5
5
  AiModelCallUsageRecord,
6
6
  AiUsageExecutionSummary,
7
7
  AiUsageStorage,
8
+ GetLatestAiModelCallForExecutionInput,
8
9
  RecordAiModelCallInput,
9
10
  RecordAiModelCallResult,
10
11
  SummarizeAiUsageExecutionInput,
@@ -69,6 +70,28 @@ export class InMemoryAiUsageStorage implements AiUsageStorage {
69
70
  return { record: structuredClone(record), created: true }
70
71
  }
71
72
 
73
+ async getLatestForExecution(
74
+ input: GetLatestAiModelCallForExecutionInput
75
+ ): Promise<AiModelCallUsageRecord | null> {
76
+ assertAiUsageExecutionId(input.executionId)
77
+ if (typeof input.projectId !== "string" || input.projectId.trim().length === 0) {
78
+ throw new TypeError("[Sixb] AI usage projectId must be nonblank.")
79
+ }
80
+
81
+ let latest: AiModelCallUsageRecord | null = null
82
+ for (const record of this.records.values()) {
83
+ if (record.projectId !== input.projectId || record.executionId !== input.executionId) continue
84
+ if (
85
+ !latest ||
86
+ record.occurredAt.getTime() > latest.occurredAt.getTime() ||
87
+ (record.occurredAt.getTime() === latest.occurredAt.getTime() && record.id > latest.id)
88
+ ) {
89
+ latest = record
90
+ }
91
+ }
92
+ return latest ? structuredClone(latest) : null
93
+ }
94
+
72
95
  async summarizeExecution(
73
96
  input: SummarizeAiUsageExecutionInput
74
97
  ): Promise<AiUsageExecutionSummary> {
@@ -13,6 +13,7 @@ export type {
13
13
  AiUsageExecutionSummary,
14
14
  AiUsageReportingStatus,
15
15
  AiUsageStorage,
16
+ GetLatestAiModelCallForExecutionInput,
16
17
  RecordAiModelCallInput,
17
18
  RecordAiModelCallResult,
18
19
  SummarizeAiUsageExecutionInput,
@@ -80,6 +80,11 @@ export interface SummarizeAiUsageExecutionsInput {
80
80
  readonly executionIds: readonly string[]
81
81
  }
82
82
 
83
+ export interface GetLatestAiModelCallForExecutionInput {
84
+ readonly projectId: string
85
+ readonly executionId: string
86
+ }
87
+
83
88
  /** Durable, provider-neutral accounting for completed language-model calls. */
84
89
  export interface AiUsageStorage {
85
90
  /**
@@ -88,6 +93,11 @@ export interface AiUsageStorage {
88
93
  */
89
94
  recordModelCall(input: RecordAiModelCallInput): Promise<RecordAiModelCallResult>
90
95
 
96
+ /** Latest completed provider call for one execution, ordered by occurrence and stable id. */
97
+ getLatestForExecution(
98
+ input: GetLatestAiModelCallForExecutionInput
99
+ ): Promise<AiModelCallUsageRecord | null>
100
+
91
101
  /** Aggregate normalized usage and call presence across every attempt of one execution. */
92
102
  summarizeExecution(input: SummarizeAiUsageExecutionInput): Promise<AiUsageExecutionSummary>
93
103
 
@@ -138,6 +138,7 @@ export type {
138
138
  AiUsageReportingStatus,
139
139
  AiUsageStorage,
140
140
  AiUsageStorageErrorCode,
141
+ GetLatestAiModelCallForExecutionInput,
141
142
  InMemoryAiUsageGroupRow,
142
143
  InMemoryAiUsageStorageSnapshot,
143
144
  RecordAiModelCallInput,
@@ -352,10 +353,13 @@ export type {
352
353
  ExpandedObjectRow,
353
354
  FacetObjectsInput,
354
355
  FacetObjectsResult,
356
+ LinkBatchKey,
355
357
  LinkDirection,
358
+ ObjectBatchKey,
356
359
  ObjectFacetBucket,
357
360
  ObjectFacetRequest,
358
361
  ObjectFacetResult,
362
+ ObjectLinkCursor,
359
363
  ObjectLinkRow,
360
364
  ObjectQueryCapabilities,
361
365
  ObjectQueryCapabilityMap,
@@ -364,9 +368,16 @@ export type {
364
368
  ObjectRow,
365
369
  ObjectRowLinks,
366
370
  ObjectStorage,
371
+ QueryObjectLinksInput,
372
+ QueryObjectLinksResult,
367
373
  QueryObjectsInput,
368
374
  QueryObjectsResult,
369
375
  } from "./objects"
376
+ export {
377
+ linkBatchKey,
378
+ objectBatchKey,
379
+ objectLinkCursor,
380
+ } from "./objects"
370
381
  export type {
371
382
  AbandonSourceMaterializationCandidateInput,
372
383
  AbandonSourceMaterializationInput,
@@ -1,6 +1,14 @@
1
1
  import type { EffectiveLinkSnapshot, EffectiveObjectSnapshot } from "../../materialization/model"
2
2
  import type { ObjectQuery, ObjectQueryPredicate, ObjectQuerySortField } from "../../objects/query"
3
3
  import { compareQueryScalarValues, queryScalarValuesEqual } from "../../objects/query/scalar-values"
4
+ import type { LinkBatchKey, ObjectBatchKey } from "./keys"
5
+ import {
6
+ compareObjectLinkCursors,
7
+ compareObjectLinks,
8
+ linkBatchKey,
9
+ objectBatchKey,
10
+ objectLinkCursor,
11
+ } from "./keys"
4
12
  import type {
5
13
  CountObjectsInput,
6
14
  CountObjectsResult,
@@ -15,6 +23,8 @@ import type {
15
23
  ObjectQueryCapabilities,
16
24
  ObjectRow,
17
25
  ObjectStorage,
26
+ QueryObjectLinksInput,
27
+ QueryObjectLinksResult,
18
28
  QueryObjectsInput,
19
29
  QueryObjectsResult,
20
30
  } from "./types"
@@ -26,6 +36,7 @@ const IN_MEMORY_OBJECT_QUERY_CAPABILITIES: ObjectQueryCapabilities = {
26
36
  facetObjects: true,
27
37
  nodes: {
28
38
  start: true,
39
+ refs: true,
29
40
  filter: true,
30
41
  text: true,
31
42
  vector: true,
@@ -80,25 +91,41 @@ const IN_MEMORY_OBJECT_QUERY_CAPABILITIES: ObjectQueryCapabilities = {
80
91
  }
81
92
 
82
93
  function objectRowKey(projectId: string, objectTypeId: string): string {
83
- return `${projectId}:${objectTypeId}`
94
+ return JSON.stringify([projectId, objectTypeId])
84
95
  }
85
96
 
86
- function comparePrimaryIds(left: string, right: string): number {
97
+ function objectRowProjectPrefix(projectId: string): string {
98
+ return `[${JSON.stringify(projectId)},`
99
+ }
100
+
101
+ function compareStrings(left: string, right: string): number {
87
102
  if (left < right) return -1
88
103
  if (left > right) return 1
89
104
  return 0
90
105
  }
91
106
 
107
+ function assertLinkQueryLimit(limit: number): void {
108
+ if (!Number.isSafeInteger(limit) || limit <= 0) {
109
+ throw new Error("Object link query limit must be a positive safe integer.")
110
+ }
111
+ }
112
+
92
113
  function sourceLinkBucketKey(projectId: string, sourceTypeId: string, sourceId: string): string {
93
- return `${projectId}:${sourceTypeId}:${sourceId}`
114
+ return JSON.stringify([projectId, sourceTypeId, sourceId])
94
115
  }
95
116
 
96
117
  function linkRowKey(linkId: string, targetTypeId: string, targetId: string): string {
97
- return `${linkId}:${targetTypeId}:${targetId}`
118
+ return JSON.stringify([linkId, targetTypeId, targetId])
98
119
  }
99
120
 
100
121
  function fullLinkRowKey(row: ObjectLinkRow): string {
101
- return `${row.sourceTypeId}:${row.sourceId}:${row.linkId}:${row.targetTypeId}:${row.targetId}`
122
+ return JSON.stringify([
123
+ row.sourceTypeId,
124
+ row.sourceId,
125
+ row.linkId,
126
+ row.targetTypeId,
127
+ row.targetId,
128
+ ])
102
129
  }
103
130
 
104
131
  type QueryEntry = {
@@ -343,6 +370,8 @@ export class InMemoryObjectStorage implements ObjectStorage {
343
370
  switch (query.kind) {
344
371
  case "start":
345
372
  return this.evaluateStart(projectId, query.objectTypeId)
373
+ case "refs":
374
+ return this.evaluateRefs(projectId, query.refs)
346
375
  case "filter": {
347
376
  const input = this.evaluateObjectQuery(projectId, query.input)
348
377
  const entries = input.entries.filter((entry) =>
@@ -446,6 +475,29 @@ export class InMemoryObjectStorage implements ObjectStorage {
446
475
  return completeEvaluation(entries)
447
476
  }
448
477
 
478
+ private evaluateRefs(
479
+ projectId: string,
480
+ refs: readonly { objectTypeId: string; primaryId: string }[]
481
+ ): QueryEvaluation {
482
+ const seen = new Set<string>()
483
+ const entries = refs
484
+ .flatMap((ref) => {
485
+ const key = JSON.stringify([ref.objectTypeId, ref.primaryId])
486
+ if (seen.has(key)) return []
487
+ seen.add(key)
488
+ const row = this.rows.get(objectRowKey(projectId, ref.objectTypeId))?.get(ref.primaryId)
489
+ return row ? [row] : []
490
+ })
491
+ .sort(
492
+ (left, right) =>
493
+ compareStrings(left.objectTypeId, right.objectTypeId) ||
494
+ compareStrings(left.primaryId, right.primaryId)
495
+ )
496
+ .map((row, order) => ({ row, score: 0, order }))
497
+
498
+ return completeEvaluation(entries)
499
+ }
500
+
449
501
  private evaluateSet(
450
502
  projectId: string,
451
503
  op: "union" | "intersect" | "subtract",
@@ -602,8 +654,8 @@ export class InMemoryObjectStorage implements ObjectStorage {
602
654
  async getByPrimaryIdBatch(params: {
603
655
  projectId: string
604
656
  items: readonly { objectTypeId: string; primaryId: string }[]
605
- }): Promise<Map<string, ObjectRow>> {
606
- const result = new Map<string, ObjectRow>()
657
+ }): Promise<Map<ObjectBatchKey, ObjectRow>> {
658
+ const result = new Map<ObjectBatchKey, ObjectRow>()
607
659
  for (const item of params.items) {
608
660
  const row = await this.getByPrimaryId({
609
661
  projectId: params.projectId,
@@ -611,7 +663,7 @@ export class InMemoryObjectStorage implements ObjectStorage {
611
663
  primaryId: item.primaryId,
612
664
  })
613
665
  if (row) {
614
- result.set(`${item.objectTypeId}:${item.primaryId}`, row)
666
+ result.set(objectBatchKey(item.objectTypeId, item.primaryId), row)
615
667
  }
616
668
  }
617
669
  return result
@@ -620,8 +672,8 @@ export class InMemoryObjectStorage implements ObjectStorage {
620
672
  async listLinksBatch(params: {
621
673
  projectId: string
622
674
  items: readonly { objectTypeId: string; objectId: string; linkId: string }[]
623
- }): Promise<Map<string, ObjectLinkRow[]>> {
624
- const result = new Map<string, ObjectLinkRow[]>()
675
+ }): Promise<Map<LinkBatchKey, ObjectLinkRow[]>> {
676
+ const result = new Map<LinkBatchKey, ObjectLinkRow[]>()
625
677
  for (const item of params.items) {
626
678
  const rows = await this.listLinks({
627
679
  projectId: params.projectId,
@@ -630,12 +682,48 @@ export class InMemoryObjectStorage implements ObjectStorage {
630
682
  linkId: item.linkId,
631
683
  })
632
684
  if (rows.length > 0) {
633
- result.set(`${item.objectTypeId}:${item.objectId}:${item.linkId}`, [...rows])
685
+ result.set(linkBatchKey(item.objectTypeId, item.objectId, item.linkId), [...rows])
634
686
  }
635
687
  }
636
688
  return result
637
689
  }
638
690
 
691
+ async queryLinks(params: QueryObjectLinksInput): Promise<QueryObjectLinksResult> {
692
+ assertLinkQueryLimit(params.limit)
693
+ if (params.objectRefs.length === 0 || params.endpointObjectTypeIds?.length === 0) {
694
+ return { links: [], hasMore: false }
695
+ }
696
+
697
+ const allowedTypes = params.endpointObjectTypeIds
698
+ ? new Set(params.endpointObjectTypeIds)
699
+ : undefined
700
+ const deduped = new Map<string, ObjectLinkRow>()
701
+ for (const object of params.objectRefs) {
702
+ const rows = await this.listLinks({
703
+ projectId: params.projectId,
704
+ objectTypeId: object.objectTypeId,
705
+ objectId: object.primaryId,
706
+ direction: params.direction,
707
+ ...(params.linkId === undefined ? {} : { linkId: params.linkId }),
708
+ })
709
+ for (const row of rows) {
710
+ if (
711
+ allowedTypes &&
712
+ (!allowedTypes.has(row.sourceTypeId) || !allowedTypes.has(row.targetTypeId))
713
+ ) {
714
+ continue
715
+ }
716
+ if (params.after && compareObjectLinkCursors(objectLinkCursor(row), params.after) <= 0) {
717
+ continue
718
+ }
719
+ deduped.set(fullLinkRowKey(row), row)
720
+ }
721
+ }
722
+
723
+ const rows = [...deduped.values()].sort(compareObjectLinks).slice(0, params.limit + 1)
724
+ return { links: rows.slice(0, params.limit), hasMore: rows.length > params.limit }
725
+ }
726
+
639
727
  async listIncidentLinksBatch(params: {
640
728
  projectId: string
641
729
  items: readonly { objectTypeId: string; objectId: string }[]
@@ -665,10 +753,9 @@ export class InMemoryObjectStorage implements ObjectStorage {
665
753
  const bucket = this.rows.get(objectRowKey(params.projectId, params.objectTypeId))
666
754
  const rows = [...(bucket?.values() ?? [])]
667
755
  .filter(
668
- (row) =>
669
- !params.afterPrimaryId || comparePrimaryIds(row.primaryId, params.afterPrimaryId) > 0
756
+ (row) => !params.afterPrimaryId || compareStrings(row.primaryId, params.afterPrimaryId) > 0
670
757
  )
671
- .sort((left, right) => comparePrimaryIds(left.primaryId, right.primaryId))
758
+ .sort((left, right) => compareStrings(left.primaryId, right.primaryId))
672
759
  .slice(0, params.limit + 1)
673
760
  const hasMore = rows.length > params.limit
674
761
  const objects = rows.slice(0, params.limit).map((row) => structuredClone(row))
@@ -707,7 +794,7 @@ export class InMemoryObjectStorage implements ObjectStorage {
707
794
  }
708
795
  } else {
709
796
  for (const [key, bucket] of this.rows) {
710
- if (key.startsWith(`${params.projectId}:`)) {
797
+ if (key.startsWith(objectRowProjectPrefix(params.projectId))) {
711
798
  allRows.push(...bucket.values())
712
799
  }
713
800
  }
@@ -1113,7 +1200,7 @@ function rowIdentityKey(row: ObjectRow): string {
1113
1200
  }
1114
1201
 
1115
1202
  function rowIdentityKeyParts(objectTypeId: string, primaryId: string): string {
1116
- return `${objectTypeId}:${primaryId}`
1203
+ return objectBatchKey(objectTypeId, primaryId)
1117
1204
  }
1118
1205
 
1119
1206
  function upsertEntry(entriesByKey: Map<string, QueryEntry>, entry: QueryEntry): void {
@@ -1,3 +1,9 @@
1
+ export type { LinkBatchKey, ObjectBatchKey } from "./keys"
2
+ export {
3
+ linkBatchKey,
4
+ objectBatchKey,
5
+ objectLinkCursor,
6
+ } from "./keys"
1
7
  export type {
2
8
  CountObjectsInput,
3
9
  CountObjectsResult,
@@ -11,6 +17,7 @@ export type {
11
17
  ObjectFacetBucket,
12
18
  ObjectFacetRequest,
13
19
  ObjectFacetResult,
20
+ ObjectLinkCursor,
14
21
  ObjectLinkRow,
15
22
  ObjectQueryCapabilities,
16
23
  ObjectQueryCapabilityMap,
@@ -19,6 +26,8 @@ export type {
19
26
  ObjectRow,
20
27
  ObjectRowLinks,
21
28
  ObjectStorage,
29
+ QueryObjectLinksInput,
30
+ QueryObjectLinksResult,
22
31
  QueryObjectsInput,
23
32
  QueryObjectsResult,
24
33
  } from "./types"
@@ -0,0 +1,36 @@
1
+ import type { ObjectLinkCursor, ObjectLinkRow } from "./types"
2
+
3
+ export type ObjectBatchKey = string & {
4
+ readonly __sixbObjectBatchKey: true
5
+ }
6
+
7
+ export type LinkBatchKey = string & {
8
+ readonly __sixbLinkBatchKey: true
9
+ }
10
+
11
+ /** Collision-safe key for object batch-read results. */
12
+ export function objectBatchKey(objectTypeId: string, primaryId: string): ObjectBatchKey {
13
+ return JSON.stringify([objectTypeId, primaryId]) as ObjectBatchKey
14
+ }
15
+
16
+ /** Collision-safe key for outgoing-link batch-read results. */
17
+ export function linkBatchKey(objectTypeId: string, objectId: string, linkId: string): LinkBatchKey {
18
+ return JSON.stringify([objectTypeId, objectId, linkId]) as LinkBatchKey
19
+ }
20
+
21
+ /** Canonical storage ordering and cursor for physical links. */
22
+ export function objectLinkCursor(link: ObjectLinkRow): ObjectLinkCursor {
23
+ return [link.sourceTypeId, link.sourceId, link.linkId, link.targetTypeId, link.targetId]
24
+ }
25
+
26
+ export function compareObjectLinkCursors(left: ObjectLinkCursor, right: ObjectLinkCursor): number {
27
+ for (let index = 0; index < left.length; index += 1) {
28
+ if (left[index] < right[index]) return -1
29
+ if (left[index] > right[index]) return 1
30
+ }
31
+ return 0
32
+ }
33
+
34
+ export function compareObjectLinks(left: ObjectLinkRow, right: ObjectLinkRow): number {
35
+ return compareObjectLinkCursors(objectLinkCursor(left), objectLinkCursor(right))
36
+ }
@@ -6,6 +6,7 @@ import type {
6
6
  ObjectQuerySortField,
7
7
  QueryScalarKind,
8
8
  } from "../../objects/query"
9
+ import type { LinkBatchKey, ObjectBatchKey } from "./keys"
9
10
 
10
11
  /**
11
12
  * Latest-state projection storage for objects and links.
@@ -64,6 +65,26 @@ export interface ObjectLinkRow {
64
65
 
65
66
  export type LinkDirection = "outgoing" | "incoming" | "both"
66
67
 
68
+ /** Stable physical-link order: source type/id, link id, then target type/id. */
69
+ export type ObjectLinkCursor = readonly [string, string, string, string, string]
70
+
71
+ export interface QueryObjectLinksInput {
72
+ projectId: string
73
+ /** Object identities are treated as a set; duplicate refs do not duplicate links. */
74
+ objectRefs: readonly { objectTypeId: string; primaryId: string }[]
75
+ direction: LinkDirection
76
+ linkId?: string
77
+ /** When present, both endpoints must belong to this set. An empty set matches no links. */
78
+ endpointObjectTypeIds?: readonly string[]
79
+ after?: ObjectLinkCursor
80
+ limit: number
81
+ }
82
+
83
+ export interface QueryObjectLinksResult {
84
+ links: readonly ObjectLinkRow[]
85
+ hasMore: boolean
86
+ }
87
+
67
88
  export type ObjectQueryCapabilityMap<T extends string> = Readonly<Partial<Record<T, boolean>>>
68
89
 
69
90
  export type ObjectQueryScalarOperation = "equality" | "ordering"
@@ -192,27 +213,34 @@ export interface ObjectStorage {
192
213
 
193
214
  /**
194
215
  * Batch fetch objects by (objectTypeId, primaryId) pairs.
195
- * Returns a Map keyed by "objectTypeId:primaryId". Missing items are absent.
216
+ * Returns a Map keyed by {@link objectBatchKey}. Missing items are absent.
196
217
  */
197
218
  getByPrimaryIdBatch(params: {
198
219
  projectId: string
199
220
  items: readonly { objectTypeId: string; primaryId: string }[]
200
- }): Promise<Map<string, ObjectRow>>
221
+ }): Promise<ReadonlyMap<ObjectBatchKey, ObjectRow>>
201
222
 
202
223
  /**
203
224
  * Batch fetch outgoing links by (objectTypeId, objectId, linkId) tuples.
204
- * Returns a Map keyed by "objectTypeId:objectId:linkId". Missing entries are absent.
225
+ * Returns a Map keyed by {@link linkBatchKey}. Missing entries are absent.
205
226
  */
206
227
  listLinksBatch(params: {
207
228
  projectId: string
208
229
  items: readonly { objectTypeId: string; objectId: string; linkId: string }[]
209
- }): Promise<Map<string, ObjectLinkRow[]>>
230
+ }): Promise<ReadonlyMap<LinkBatchKey, ObjectLinkRow[]>>
231
+
232
+ /**
233
+ * Read one canonical page of physical links incident to an object set.
234
+ * Filtering, endpoint-type scope, cursoring, ordering, and the limit are provider-owned so a
235
+ * bounded response never requires loading every incident link into the runtime.
236
+ */
237
+ queryLinks(params: QueryObjectLinksInput): Promise<QueryObjectLinksResult>
210
238
 
211
239
  /**
212
240
  * Batch fetch every link incident to any of the given objects, in BOTH directions (the object as
213
241
  * link source or target). Returns a flat, de-duplicated list: a physical link incident to two
214
- * listed objects appears once. Used to load cascade-delete state for `object.delete` operations,
215
- * which {@link listLinksBatch} cannot express because it keys on a specific linkId.
242
+ * listed objects appears once. This unpaged primitive exists for internal materialization and
243
+ * per-parent expansion work; interactive reads use {@link queryLinks}.
216
244
  */
217
245
  listIncidentLinksBatch(params: {
218
246
  projectId: string
@@ -155,9 +155,9 @@ function assertProjectionTelemetryBatch(
155
155
  if (source.sourceRowsSkipped > source.sourceRowCount) {
156
156
  invalidCorrelation("Projection telemetry skipped rows exceed its source row count.")
157
157
  }
158
- if (source.sourceRowCount !== inputPointCount + source.sourceRowsSkipped) {
158
+ if (inputPointCount < source.sourceRowCount - source.sourceRowsSkipped) {
159
159
  invalidCorrelation(
160
- "Projection telemetry source row count does not match input points plus skipped rows."
160
+ "Projection telemetry input points do not account for every non-skipped source row."
161
161
  )
162
162
  }
163
163
  if (typeof source.inputExhausted !== "boolean") {
@@ -1077,6 +1077,18 @@ export function runAgentStorageContractSuite<TStorage extends AgentStorageContra
1077
1077
  await storage.checkpoints.getLatest({ projectId: otherProjectId, threadId: "thr_1" })
1078
1078
  ).toBeNull()
1079
1079
  expect(await storage.checkpoints.getLatest({ projectId, threadId: "thr_2" })).toBeNull()
1080
+ await expect(
1081
+ storage.checkpoints.getByRunIds({
1082
+ projectId,
1083
+ runIds: ["run_2", "missing", "run_2"],
1084
+ })
1085
+ ).resolves.toEqual([created])
1086
+ await expect(
1087
+ storage.checkpoints.getByRunIds({
1088
+ projectId: otherProjectId,
1089
+ runIds: ["run_2"],
1090
+ })
1091
+ ).resolves.toEqual([])
1080
1092
 
1081
1093
  const retained = await storage.messages.list({
1082
1094
  projectId,