@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
@@ -4,6 +4,7 @@ import type { SixbFailure } from "../errors/types"
4
4
  import { isJsonValue, isPlainRecord, type JsonValue } from "../json"
5
5
  import {
6
6
  AGENT_RUN_FAILURE_CODES,
7
+ type AgentContextCheckpointReason,
7
8
  type AgentRunFailureCode,
8
9
  type AgentRunRecord,
9
10
  } from "../storage/agents/types"
@@ -189,11 +190,35 @@ export async function subscribeAgentRunCancel(
189
190
  /** Exact portable failure exposed by a terminal Agent run stream event. */
190
191
  export type AgentRunFailure = SixbFailure<AgentRunFailureCode>
191
192
 
193
+ export const AGENT_COMPACTION_FAILURE_CODES = [
194
+ "context_limit_exceeded",
195
+ "summary_failed",
196
+ "checkpoint_failed",
197
+ ] as const
198
+ export type AgentCompactionFailureCode = (typeof AGENT_COMPACTION_FAILURE_CODES)[number]
199
+
192
200
  export type AgentRunStreamEvent =
193
201
  | (AgentRunStreamEventBase & {
194
202
  readonly type: "agent.run.started"
195
203
  readonly modelId?: string
196
204
  })
205
+ | (AgentRunStreamEventBase & {
206
+ readonly type: "agent.compaction.started"
207
+ readonly reason: AgentContextCheckpointReason
208
+ readonly estimatedInputTokensBefore: number
209
+ })
210
+ | (AgentRunStreamEventBase & {
211
+ readonly type: "agent.compaction.completed"
212
+ readonly reason: AgentContextCheckpointReason
213
+ readonly checkpointId: string
214
+ readonly estimatedInputTokensBefore: number
215
+ readonly estimatedInputTokensAfter: number
216
+ })
217
+ | (AgentRunStreamEventBase & {
218
+ readonly type: "agent.compaction.failed"
219
+ readonly reason: AgentContextCheckpointReason
220
+ readonly errorCode: AgentCompactionFailureCode
221
+ })
197
222
  | (AgentRunStreamEventBase & {
198
223
  readonly type: "agent.ui.chunk"
199
224
  readonly chunkIndex: number
@@ -263,6 +288,23 @@ export function isAgentRunStreamEvent(value: unknown): value is AgentRunStreamEv
263
288
  switch (value.type) {
264
289
  case "agent.run.started":
265
290
  return value.modelId === undefined || typeof value.modelId === "string"
291
+ case "agent.compaction.started":
292
+ return (
293
+ isAgentContextCheckpointReason(value.reason) &&
294
+ isTokenEstimate(value.estimatedInputTokensBefore)
295
+ )
296
+ case "agent.compaction.completed":
297
+ return (
298
+ isAgentContextCheckpointReason(value.reason) &&
299
+ typeof value.checkpointId === "string" &&
300
+ isTokenEstimate(value.estimatedInputTokensBefore) &&
301
+ isTokenEstimate(value.estimatedInputTokensAfter)
302
+ )
303
+ case "agent.compaction.failed":
304
+ return (
305
+ isAgentContextCheckpointReason(value.reason) &&
306
+ AGENT_COMPACTION_FAILURE_CODES.includes(value.errorCode as AgentCompactionFailureCode)
307
+ )
266
308
  case "agent.ui.chunk":
267
309
  return (
268
310
  Number.isInteger(value.chunkIndex) &&
@@ -282,6 +324,14 @@ export function isAgentRunStreamEvent(value: unknown): value is AgentRunStreamEv
282
324
  }
283
325
  }
284
326
 
327
+ function isAgentContextCheckpointReason(value: unknown): value is AgentContextCheckpointReason {
328
+ return value === "threshold" || value === "overflow"
329
+ }
330
+
331
+ function isTokenEstimate(value: unknown): value is number {
332
+ return Number.isSafeInteger(value) && (value as number) >= 0
333
+ }
334
+
285
335
  function isTerminalAgentRunStatus(value: unknown): value is "succeeded" | "failed" | "cancelled" {
286
336
  return value === "succeeded" || value === "failed" || value === "cancelled"
287
337
  }
@@ -305,6 +355,12 @@ export function agentRunStreamIdempotencyKey(event: AgentRunStreamEvent): string
305
355
  switch (event.type) {
306
356
  case "agent.run.started":
307
357
  return `${event.runId}:${event.attempt}:started`
358
+ case "agent.compaction.started":
359
+ return `${event.runId}:${event.attempt}:compaction:${event.reason}:started`
360
+ case "agent.compaction.completed":
361
+ return `${event.runId}:${event.attempt}:compaction:${event.checkpointId}:completed`
362
+ case "agent.compaction.failed":
363
+ return `${event.runId}:${event.attempt}:compaction:${event.reason}:failed:${event.errorCode}`
308
364
  case "agent.ui.chunk":
309
365
  return `${event.runId}:${event.attempt}:chunk:${event.chunkIndex}`
310
366
  case "agent.message.finalized":
@@ -26,6 +26,15 @@ export const AGENT_REASONING_LEVELS = [
26
26
  "xhigh",
27
27
  ] as const satisfies readonly AgentReasoningLevel[]
28
28
 
29
+ export interface AgentContextConfig {
30
+ /** Override the provider context-window size inferred by the agent worker. */
31
+ readonly windowTokens?: number
32
+ /** Tokens reserved for the next response and compaction work. */
33
+ readonly reserveTokens?: number
34
+ /** Approximate recent-message budget retained verbatim after compaction. */
35
+ readonly keepRecentTokens?: number
36
+ }
37
+
29
38
  /**
30
39
  * Loop / stop controls for an agent run.
31
40
  *
@@ -35,6 +44,7 @@ export interface AgentLoopConfig {
35
44
  readonly stopWhen?: {
36
45
  readonly maxSteps?: number
37
46
  }
47
+ readonly context?: AgentContextConfig
38
48
  }
39
49
 
40
50
  type Simplify<T> = { [K in keyof T]: T[K] } & {}
@@ -10,6 +10,7 @@ import type { GroupDefinition, SecurityDefinitionCatalog } from "../security"
10
10
  import { AgentDefinitionError } from "./errors"
11
11
  import {
12
12
  AGENT_REASONING_LEVELS,
13
+ type AgentContextConfig,
13
14
  type AgentDefinition,
14
15
  type AgentLoopConfig,
15
16
  type AgentReasoningLevel,
@@ -159,14 +160,43 @@ export function validateAgentToolsAtStartup(agents: readonly AgentDefinition[]):
159
160
  }
160
161
  }
161
162
 
162
- export function assertValidLoopConfig(loop: AgentLoopConfig | undefined): void {
163
+ export function resolveAgentLoopConfig(
164
+ loop: AgentLoopConfig | undefined
165
+ ): AgentLoopConfig | undefined {
166
+ if (loop === undefined) return undefined
167
+
163
168
  const maxSteps = loop?.stopWhen?.maxSteps
164
- if (maxSteps === undefined) {
165
- return
169
+ if (maxSteps !== undefined && (!Number.isSafeInteger(maxSteps) || maxSteps <= 0)) {
170
+ throw new AgentDefinitionError(
171
+ "[Sixb] Agent loop.stopWhen.maxSteps must be a positive safe integer."
172
+ )
173
+ }
174
+
175
+ const context = loop.context ? snapshotAgentContextConfig(loop.context) : undefined
176
+ return Object.freeze({
177
+ ...(loop.stopWhen === undefined
178
+ ? {}
179
+ : { stopWhen: Object.freeze({ ...(maxSteps === undefined ? {} : { maxSteps }) }) }),
180
+ ...(context === undefined ? {} : { context }),
181
+ })
182
+ }
183
+
184
+ function snapshotAgentContextConfig(context: AgentContextConfig): AgentContextConfig {
185
+ const snapshot: Record<string, number> = {}
186
+ for (const field of ["windowTokens", "reserveTokens", "keepRecentTokens"] as const) {
187
+ const value = context[field]
188
+ if (value === undefined) continue
189
+ assertPositiveSafeInteger(value, field)
190
+ snapshot[field] = value
166
191
  }
167
- if (!Number.isInteger(maxSteps) || maxSteps <= 0) {
192
+
193
+ return Object.freeze(snapshot)
194
+ }
195
+
196
+ function assertPositiveSafeInteger(value: unknown, field: string): asserts value is number {
197
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) {
168
198
  throw new AgentDefinitionError(
169
- "[Sixb] Agent loop.stopWhen.maxSteps must be a positive finite integer."
199
+ `[Sixb] Agent loop.context.${field} must be a positive safe integer.`
170
200
  )
171
201
  }
172
202
  }
@@ -131,6 +131,13 @@ export const SIXB_API_ROUTES: readonly SixbApiRoute[] = [
131
131
  accessToken: true,
132
132
  agentApi: true,
133
133
  },
134
+ {
135
+ operationId: "queryObjectLinks",
136
+ method: "POST",
137
+ path: "/api/objects/query/links",
138
+ accessToken: true,
139
+ agentApi: true,
140
+ },
134
141
  {
135
142
  operationId: "countObjects",
136
143
  method: "POST",
@@ -159,13 +166,6 @@ export const SIXB_API_ROUTES: readonly SixbApiRoute[] = [
159
166
  accessToken: true,
160
167
  agentApi: true,
161
168
  },
162
- {
163
- operationId: "listObjectLinks",
164
- method: "GET",
165
- path: "/api/objects/:objectTypeId/:objectId/links",
166
- accessToken: true,
167
- agentApi: true,
168
- },
169
169
  // The four write routes. Bearer-capable because their execution facade enforces authorization
170
170
  // (`edit:object` / `append:telemetry`), which is exactly the precondition this table requires.
171
171
  // NOT agent-proxied: letting an agent turn write objects on its own is a separate decision, and
package/src/index.ts CHANGED
@@ -574,6 +574,7 @@ export type {
574
574
  ObjectQueryPredicateIn,
575
575
  ObjectQueryPredicateNot,
576
576
  ObjectQueryProject,
577
+ ObjectQueryRefs,
577
578
  ObjectQueryResultShape,
578
579
  ObjectQuerySet,
579
580
  ObjectQuerySetOperation,
@@ -854,6 +855,8 @@ export type {
854
855
  export type {
855
856
  ExecutionObjectByIdHandle,
856
857
  ExecutionObjectSet,
858
+ ObjectQueryLinksInput,
859
+ ObjectQueryLinksResult,
857
860
  ObjectsRuntime,
858
861
  } from "./objects/execution"
859
862
  export type { PipelineRunsRuntime, PipelinesRuntime } from "./pipelines/execution"
@@ -941,6 +944,7 @@ export type {
941
944
  // ── Agents ──────────────────────────────────────────────────
942
945
 
943
946
  export type {
947
+ AgentContextConfig,
944
948
  AgentContextEntryInput,
945
949
  AgentContextInput,
946
950
  AgentContextOrigin,
@@ -1013,6 +1017,15 @@ export {
1013
1017
  requestAgentRun,
1014
1018
  } from "./agents"
1015
1019
 
1020
+ // ── Models ──────────────────────────────────────────────────
1021
+
1022
+ export type {
1023
+ LanguageModelCatalog,
1024
+ LanguageModelEntry,
1025
+ ModelCatalog,
1026
+ ModelCatalogInput,
1027
+ } from "./models"
1028
+
1016
1029
  // ── Scheduling ──────────────────────────────────────────────
1017
1030
 
1018
1031
  export { CronValidationError } from "./schedules"
@@ -1034,6 +1047,7 @@ export type {
1034
1047
  ProjectionTargetByKind,
1035
1048
  SourceEditConflictResolution,
1036
1049
  TelemetryProjectionDefinition,
1050
+ TelemetryProjectionPropertyMapping,
1037
1051
  } from "./projections"
1038
1052
 
1039
1053
  export {
@@ -84,7 +84,8 @@ export interface DatasetWriteCommitResult extends DatasetVersion {
84
84
  * report `unchanged` and reuse it for commits that cannot change visible
85
85
  * dataset content: a snapshot whose rows equal the latest visible rows
86
86
  * (order-insensitive, with equal duplicate counts), or an append of zero
87
- * rows.
87
+ * rows. Without a latest version, an empty snapshot or append still creates
88
+ * the first addressable dataset version.
88
89
  */
89
90
  readonly outcome: "created" | "unchanged"
90
91
  }
@@ -90,7 +90,6 @@ function prepareTelemetryAppend(
90
90
  points: raw.input.points.map((point) => validateTelemetryPoint(context.ontology, point)),
91
91
  })
92
92
  const inputPointCount = telemetryInputPointCount(input, rawInputPointCount)
93
- validateTelemetryBatch(input, inputPointCount)
94
93
 
95
94
  const ontologyRevision = context.projectionRegistry.ontologyRevision
96
95
  const execution = prepareMaterializerExecution(context.projectId, raw.scope)
@@ -114,21 +113,6 @@ function telemetryInputPointCount(
114
113
  return input.points.length
115
114
  }
116
115
 
117
- function validateTelemetryBatch(input: NormalizedTelemetryAppend, inputPointCount: number): void {
118
- if (input.source.kind === "projection") {
119
- if (input.source.sourceRowCount === 0) {
120
- throw new MaterializationValidationError(
121
- "Projection telemetry batches must consume at least one source row; an empty dataset produces no batch commit."
122
- )
123
- }
124
- if (input.source.sourceRowCount !== inputPointCount + input.source.sourceRowsSkipped) {
125
- throw new MaterializationValidationError(
126
- "Projection telemetry sourceRowCount must equal input points plus skipped rows."
127
- )
128
- }
129
- }
130
- }
131
-
132
116
  function prepareRuntimeTelemetry(
133
117
  context: Pick<MaterializerContext, "projectId" | "clock">,
134
118
  input: NormalizedTelemetryAppend,
@@ -157,6 +141,7 @@ function prepareProjectionTelemetry(
157
141
  )
158
142
  validateTelemetryProjectionDataset(resolvedProjection, source)
159
143
  validateTelemetryOwnership(resolvedProjection, input)
144
+ validateTelemetryBatchCardinality(resolvedProjection, source, inputPointCount)
160
145
 
161
146
  const runIdentity = createProjectionRunMaterializationIdentity({
162
147
  resolved: resolvedProjection,
@@ -182,6 +167,38 @@ function prepareProjectionTelemetry(
182
167
  }
183
168
  }
184
169
 
170
+ function validateTelemetryBatchCardinality(
171
+ resolved: ResolvedTelemetryProjection,
172
+ source: ProjectionTelemetrySource,
173
+ inputPointCount: number
174
+ ): void {
175
+ if (source.sourceRowCount === 0) {
176
+ throw new MaterializationValidationError(
177
+ "Projection telemetry batches must consume at least one source row; an empty dataset produces no batch commit."
178
+ )
179
+ }
180
+
181
+ const mappedPropertyCount = resolved.ownership.telemetry.length
182
+ if (mappedPropertyCount === 0) {
183
+ throw new MaterializationValidationError(
184
+ `Telemetry projection '${resolved.projectionId}' must own at least one telemetry property.`
185
+ )
186
+ }
187
+
188
+ const sourceRowsEmitted = source.sourceRowCount - source.sourceRowsSkipped
189
+ const maximumPointCount = sourceRowsEmitted * mappedPropertyCount
190
+ if (!Number.isSafeInteger(maximumPointCount)) {
191
+ throw new MaterializationValidationError(
192
+ `Telemetry projection '${resolved.projectionId}' batch point capacity exceeds the safe integer range.`
193
+ )
194
+ }
195
+ if (inputPointCount < sourceRowsEmitted || inputPointCount > maximumPointCount) {
196
+ throw new MaterializationValidationError(
197
+ `Telemetry projection '${resolved.projectionId}' must emit between ${sourceRowsEmitted} and ${maximumPointCount} points for ${source.sourceRowCount} source rows with ${source.sourceRowsSkipped} skipped.`
198
+ )
199
+ }
200
+ }
201
+
185
202
  function validateTelemetryProjectionDataset(
186
203
  resolved: ResolvedTelemetryProjection,
187
204
  source: ProjectionTelemetrySource
@@ -0,0 +1,71 @@
1
+ import type { LanguageModelV4 } from "@ai-sdk/provider"
2
+ import { createDefinitionCatalog, type DefinitionCatalog } from "../runtime/definitions"
3
+ import { RuntimeError } from "../runtime/errors"
4
+
5
+ /** The configured language models, with the project default. */
6
+ export interface LanguageModelCatalog extends DefinitionCatalog<LanguageModelEntry> {
7
+ readonly default: LanguageModelEntry
8
+ }
9
+
10
+ /**
11
+ * One configured language model.
12
+ *
13
+ * `ref` is derived, never author-supplied, and stays internal to the runtime: it identifies the
14
+ * configured binding, not a vendor model. The AI SDK's `provider` names the binding, so
15
+ * `openai.chat(...)` and `openai.responses(...)` are distinct entries even for one vendor model,
16
+ * and neither matches the canonical id the worker resolves for billing
17
+ * (`SDK_PROVIDER_BINDINGS` in `@sixb/agent-worker`). Anything user-facing should key on
18
+ * `provider` + `modelId` instead.
19
+ */
20
+ export interface LanguageModelEntry {
21
+ readonly ref: string
22
+ readonly provider: string
23
+ readonly modelId: string
24
+ readonly model: LanguageModelV4
25
+ }
26
+
27
+ /** Models a project allows Sixb to use, organized by technical model kind. */
28
+ export interface ModelCatalog {
29
+ readonly language: LanguageModelCatalog
30
+ }
31
+
32
+ export interface ModelCatalogInput {
33
+ /** Ordered; the first entry is the project default. */
34
+ readonly language: readonly LanguageModelV4[]
35
+ }
36
+
37
+ /**
38
+ * Identify a catalog entry by the binding its model was constructed from.
39
+ *
40
+ * `gateway("openai/gpt-5.4")` and `openai("gpt-5.4")` are different entries on purpose: they route
41
+ * differently and bill differently. See {@link LanguageModelEntry}.
42
+ */
43
+ export function modelRef(model: LanguageModelV4): string {
44
+ return `${model.provider}/${model.modelId}`
45
+ }
46
+
47
+ /** Build the immutable project model catalog. Rejects duplicates and an empty catalog. */
48
+ export function createModelCatalog(input: ModelCatalogInput): ModelCatalog {
49
+ const byRef = new Map<string, LanguageModelEntry>()
50
+
51
+ for (const model of input.language) {
52
+ const ref = modelRef(model)
53
+ if (byRef.has(ref)) {
54
+ throw new RuntimeError(
55
+ `[Sixb] Duplicate language model '${ref}' in 'models'. Each provider and model id pair may be configured once.`
56
+ )
57
+ }
58
+ byRef.set(ref, Object.freeze({ ref, provider: model.provider, modelId: model.modelId, model }))
59
+ }
60
+
61
+ const [defaultEntry] = byRef.values()
62
+ if (defaultEntry === undefined) {
63
+ throw new RuntimeError(
64
+ "[Sixb] 'models.language' needs at least one model. Configure one or omit 'models' from createSixb()."
65
+ )
66
+ }
67
+
68
+ return Object.freeze({
69
+ language: Object.freeze({ ...createDefinitionCatalog(byRef), default: defaultEntry }),
70
+ })
71
+ }
@@ -29,8 +29,11 @@ import {
29
29
  type ExecuteObjectFacetsInput,
30
30
  type ExecuteObjectFacetsResult,
31
31
  type ExecuteObjectQueryInput,
32
+ type ExecuteObjectQueryLinksInput,
33
+ type ExecuteObjectQueryLinksResult,
32
34
  type ExecuteObjectQueryResult,
33
35
  executeObjectQuery,
36
+ executeObjectQueryLinks,
34
37
  existsObjects,
35
38
  facetObjects,
36
39
  } from "./query"
@@ -121,6 +124,12 @@ export interface ExecutionObjectSet<
121
124
  byId(id: string): ExecutionObjectByIdHandle<TObjectType, TValueTypes>
122
125
  }
123
126
 
127
+ /** Public input for querying physical links incident to an object query result. */
128
+ export type ObjectQueryLinksInput = Omit<ExecuteObjectQueryLinksInput, "projectId">
129
+
130
+ /** Public result returned by {@link ObjectsRuntime.queryLinks}. */
131
+ export type ObjectQueryLinksResult = ExecuteObjectQueryLinksResult
132
+
124
133
  export interface ObjectsRuntime<TOntologySources extends readonly OntologySource[]>
125
134
  extends ExecutionObjectOperations {
126
135
  <TObjectType extends RegisteredObjectType<TOntologySources>>(
@@ -131,6 +140,7 @@ export interface ObjectsRuntime<TOntologySources extends readonly OntologySource
131
140
  RegisteredObjectType<TOntologySources>
132
141
  >
133
142
  executeQuery(input: Omit<ExecuteObjectQueryInput, "projectId">): Promise<ExecuteObjectQueryResult>
143
+ queryLinks(input: ObjectQueryLinksInput): Promise<ObjectQueryLinksResult>
134
144
  count(input: Omit<ExecuteObjectCountInput, "projectId">): Promise<ExecuteObjectCountResult>
135
145
  exists(input: Omit<ExecuteObjectExistsInput, "projectId">): Promise<ExecuteObjectExistsResult>
136
146
  facet(input: Omit<ExecuteObjectFacetsInput, "projectId">): Promise<ExecuteObjectFacetsResult>
@@ -208,6 +218,16 @@ export function createObjectsRuntime<TOntologySources extends readonly OntologyS
208
218
  authorization: runtime.authorization,
209
219
  }
210
220
  ),
221
+ queryLinks: (input: ObjectQueryLinksInput) =>
222
+ executeObjectQueryLinks(
223
+ { projectId: runtime.projectId, ...input },
224
+ {
225
+ ontology: runtime.ontology,
226
+ storage: runtime.storage.objects,
227
+ runtimeAuthorization: runtime.runtimeAuthorization,
228
+ authorization: runtime.authorization,
229
+ }
230
+ ),
211
231
  count: (input: Omit<ExecuteObjectCountInput, "projectId">) =>
212
232
  countObjects(
213
233
  { projectId: runtime.projectId, ...input },
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { SixbRuntimeContext } from "../../runtime/types"
10
+ import { objectBatchKey } from "../../storage"
10
11
  import { ObjectNotFoundError } from "../../storage/errors"
11
12
 
12
13
  export interface LinkEndpointItem {
@@ -62,5 +63,5 @@ export function requireEndpoints(item: LinkEndpointItem, existing: EndpointExist
62
63
  }
63
64
 
64
65
  function endpointKey(objectTypeId: string, primaryId: string): string {
65
- return `${objectTypeId}:${primaryId}`
66
+ return objectBatchKey(objectTypeId, primaryId)
66
67
  }
@@ -7,6 +7,7 @@ import type {
7
7
  ExpandedLinkValue,
8
8
  ExpandedObjectRow,
9
9
  FacetObjectsResult,
10
+ ObjectBatchKey,
10
11
  ObjectFacetRequest,
11
12
  ObjectFacetResult,
12
13
  ObjectLinkRow,
@@ -16,6 +17,7 @@ import type {
16
17
  ObjectStorage,
17
18
  QueryObjectsResult,
18
19
  } from "../../storage"
20
+ import { linkBatchKey, objectBatchKey } from "../../storage"
19
21
  import {
20
22
  ObjectQueryExecutionError,
21
23
  ObjectQueryPlanningError,
@@ -52,6 +54,7 @@ export interface QueryExecutorOptions
52
54
  storage: ObjectStorage
53
55
  maxLimit?: number
54
56
  maxPageSize?: number
57
+ maxRefs?: number
55
58
  maxFacetLimit?: number
56
59
  /**
57
60
  * Per-parent cap on how many links a single `expand` hydrates, applied as a
@@ -132,6 +135,7 @@ export async function executeObjectQuery(
132
135
  ontology: options.ontology,
133
136
  maxLimit: options.maxLimit,
134
137
  maxPageSize: options.maxPageSize,
138
+ maxRefs: options.maxRefs,
135
139
  normalize: false,
136
140
  })
137
141
  assertQueryViewable(validated, options)
@@ -199,6 +203,7 @@ export async function countObjects(
199
203
  ontology: options.ontology,
200
204
  maxLimit: options.maxLimit,
201
205
  maxPageSize: options.maxPageSize,
206
+ maxRefs: options.maxRefs,
202
207
  normalize: false,
203
208
  })
204
209
  assertQueryViewable(validated, options)
@@ -259,6 +264,7 @@ export async function existsObjects(
259
264
  ontology: options.ontology,
260
265
  maxLimit: options.maxLimit,
261
266
  maxPageSize: options.maxPageSize,
267
+ maxRefs: options.maxRefs,
262
268
  normalize: false,
263
269
  })
264
270
  assertQueryViewable(validated, options)
@@ -319,6 +325,7 @@ export async function facetObjects(
319
325
  ontology: options.ontology,
320
326
  maxLimit: options.maxLimit,
321
327
  maxPageSize: options.maxPageSize,
328
+ maxRefs: options.maxRefs,
322
329
  normalize: false,
323
330
  })
324
331
  assertQueryViewable(validated, options)
@@ -435,6 +442,7 @@ function resolveExpansions(query: ObjectQuery, ctx: ExpansionResolutionContext):
435
442
  }
436
443
  }
437
444
  case "start":
445
+ case "refs":
438
446
  return query
439
447
  case "set":
440
448
  return {
@@ -555,6 +563,8 @@ function expandIncludeSubtypes(query: ObjectQuery, ontology: OntologyRegistry):
555
563
  inputs: objectTypeIds.map((objectTypeId) => ({ kind: "start", objectTypeId })),
556
564
  }
557
565
  }
566
+ case "refs":
567
+ return query
558
568
  case "filter":
559
569
  case "text":
560
570
  case "vector":
@@ -598,6 +608,8 @@ async function evaluateFallbackQuery(
598
608
  switch (query.kind) {
599
609
  case "start":
600
610
  return evaluateFallbackStart(projectId, query, options, maxRows)
611
+ case "refs":
612
+ return evaluateFallbackRefs(projectId, query, options)
601
613
  case "filter": {
602
614
  const input = await evaluateFallbackQuery(projectId, query.input, options, maxRows)
603
615
  return completeFallbackEvaluation(
@@ -672,6 +684,23 @@ async function evaluateFallbackQuery(
672
684
  }
673
685
  }
674
686
 
687
+ async function evaluateFallbackRefs(
688
+ projectId: string,
689
+ query: Extract<ObjectQuery, { kind: "refs" }>,
690
+ options: QueryExecutorOptions
691
+ ): Promise<FallbackEvaluation> {
692
+ const rows = await options.storage.getByPrimaryIdBatch({
693
+ projectId,
694
+ items: query.refs,
695
+ })
696
+ return completeFallbackEvaluation(
697
+ query.refs.flatMap((ref, order) => {
698
+ const row = rows.get(objectBatchKey(ref.objectTypeId, ref.primaryId))
699
+ return row ? [{ row, order }] : []
700
+ })
701
+ )
702
+ }
703
+
675
704
  /**
676
705
  * A candidate neighbour for one parent under a single expansion: the object to
677
706
  * hydrate plus the metadata carried on the link edge. For outgoing expansions
@@ -769,7 +798,9 @@ async function collectOutgoingEdges(
769
798
  })
770
799
 
771
800
  return parents.map((parent) => {
772
- const links = linksByKey.get(`${parent.objectTypeId}:${parent.primaryId}:${expansion.linkId}`)
801
+ const links = linksByKey.get(
802
+ linkBatchKey(parent.objectTypeId, parent.primaryId, expansion.linkId)
803
+ )
773
804
  if (!links) return []
774
805
  return links.map((link) => ({
775
806
  neighborTypeId: link.targetTypeId,
@@ -821,8 +852,8 @@ async function fetchExpansionTargets(
821
852
  projectId: string,
822
853
  edgesByParent: readonly ExpansionEdge[][],
823
854
  options: QueryExecutorOptions
824
- ): Promise<Map<string, ObjectRow>> {
825
- const seen = new Set<string>()
855
+ ): Promise<ReadonlyMap<ObjectBatchKey, ObjectRow>> {
856
+ const seen = new Set<ObjectBatchKey>()
826
857
  const items: { objectTypeId: string; primaryId: string }[] = []
827
858
  for (const edges of edgesByParent) {
828
859
  for (const edge of edges) {
@@ -841,10 +872,10 @@ async function fetchExpansionTargets(
841
872
  async function enrichExpansionTargets(
842
873
  projectId: string,
843
874
  trimmedByParent: readonly ExpansionEdge[][],
844
- baseTargets: Map<string, ObjectRow>,
875
+ baseTargets: ReadonlyMap<ObjectBatchKey, ObjectRow>,
845
876
  expansion: ObjectExpansion,
846
877
  options: QueryExecutorOptions
847
- ): Promise<Map<string, ObjectRow>> {
878
+ ): Promise<ReadonlyMap<ObjectBatchKey, ObjectRow>> {
848
879
  if (!expansion.expand || expansion.expand.length === 0) {
849
880
  return baseTargets
850
881
  }
@@ -862,7 +893,7 @@ async function enrichExpansionTargets(
862
893
  }
863
894
 
864
895
  const enriched = await hydrateExpansions(projectId, uniqueTargets, expansion.expand, options)
865
- const enrichedByKey = new Map<string, ObjectRow>()
896
+ const enrichedByKey = new Map<ObjectBatchKey, ObjectRow>()
866
897
  for (const row of enriched) {
867
898
  enrichedByKey.set(targetKey(row.objectTypeId, row.primaryId), row)
868
899
  }
@@ -872,7 +903,7 @@ async function enrichExpansionTargets(
872
903
  function trimExpansionEdges(
873
904
  edges: readonly ExpansionEdge[],
874
905
  expansion: ObjectExpansion,
875
- baseTargets: Map<string, ObjectRow>,
906
+ baseTargets: ReadonlyMap<ObjectBatchKey, ObjectRow>,
876
907
  options: QueryExecutorOptions
877
908
  ): ExpansionEdge[] {
878
909
  let ordered = edges
@@ -899,7 +930,7 @@ function compareExpansionEdges(
899
930
  left: ExpansionEdge,
900
931
  right: ExpansionEdge,
901
932
  fields: readonly ObjectQuerySortField[],
902
- baseTargets: Map<string, ObjectRow>
933
+ baseTargets: ReadonlyMap<ObjectBatchKey, ObjectRow>
903
934
  ): number {
904
935
  const leftRow = baseTargets.get(targetKey(left.neighborTypeId, left.neighborId))
905
936
  const rightRow = baseTargets.get(targetKey(right.neighborTypeId, right.neighborId))
@@ -954,8 +985,8 @@ function toLinkValue(
954
985
  return rows
955
986
  }
956
987
 
957
- function targetKey(objectTypeId: string, id: string): string {
958
- return `${objectTypeId}:${id}`
988
+ function targetKey(objectTypeId: string, id: string): ObjectBatchKey {
989
+ return objectBatchKey(objectTypeId, id)
959
990
  }
960
991
 
961
992
  async function evaluateFallbackStart(
@@ -1300,7 +1331,7 @@ function facetValueSortKey(value: unknown): string {
1300
1331
  }
1301
1332
 
1302
1333
  function rowIdentityKey(row: ObjectRow): string {
1303
- return `${row.objectTypeId}:${row.primaryId}`
1334
+ return objectBatchKey(row.objectTypeId, row.primaryId)
1304
1335
  }
1305
1336
 
1306
1337
  function encodePageOffset(offset: number): string {