@sixb/sqlite 0.1.25 → 0.2.0

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 (44) hide show
  1. package/dist/agents/agent-storage.d.ts +8 -3
  2. package/dist/agents/agent-storage.d.ts.map +1 -1
  3. package/dist/agents/computers.d.ts +22 -0
  4. package/dist/agents/computers.d.ts.map +1 -0
  5. package/dist/agents/messages.d.ts.map +1 -1
  6. package/dist/agents/rows.d.ts +6 -0
  7. package/dist/agents/rows.d.ts.map +1 -1
  8. package/dist/agents/runs.d.ts +2 -1
  9. package/dist/agents/runs.d.ts.map +1 -1
  10. package/dist/agents/threads.d.ts +4 -1
  11. package/dist/agents/threads.d.ts.map +1 -1
  12. package/dist/agents/token-rates.d.ts +13 -0
  13. package/dist/agents/token-rates.d.ts.map +1 -0
  14. package/dist/ai-accounting-filters.d.ts +14 -0
  15. package/dist/ai-accounting-filters.d.ts.map +1 -0
  16. package/dist/ai-cost-groups.d.ts +1 -1
  17. package/dist/ai-cost-groups.d.ts.map +1 -1
  18. package/dist/ai-cost-storage.d.ts.map +1 -1
  19. package/dist/index.js +465 -63
  20. package/dist/index.js.map +17 -14
  21. package/dist/migrations.d.ts.map +1 -1
  22. package/dist/objects/query-compiler.d.ts.map +1 -1
  23. package/dist/ontology-storage/commits.d.ts.map +1 -1
  24. package/dist/ontology-storage/materializations.d.ts +2 -1
  25. package/dist/ontology-storage/materializations.d.ts.map +1 -1
  26. package/package.json +3 -3
  27. package/src/agents/agent-storage.ts +10 -3
  28. package/src/agents/computers.ts +112 -0
  29. package/src/agents/messages.ts +13 -0
  30. package/src/agents/rows.ts +14 -0
  31. package/src/agents/runs.ts +52 -4
  32. package/src/agents/threads.ts +108 -4
  33. package/src/agents/token-rates.ts +90 -0
  34. package/src/ai-accounting-filters.ts +51 -0
  35. package/src/ai-cost-groups.ts +52 -14
  36. package/src/ai-cost-storage.ts +101 -39
  37. package/src/migrations/080-agent-run-cancel-requests.sql +3 -0
  38. package/src/migrations/081-agent-thread-runs-on.sql +4 -0
  39. package/src/migrations/082-agent-token-rates.sql +17 -0
  40. package/src/migrations/083-agent-computer-sessions.sql +27 -0
  41. package/src/migrations.ts +12 -0
  42. package/src/objects/query-compiler.ts +44 -28
  43. package/src/ontology-storage/commits.ts +9 -0
  44. package/src/ontology-storage/materializations.ts +18 -1
@@ -9,6 +9,7 @@ import {
9
9
  import {
10
10
  hasVectorProfile,
11
11
  isVectorProfileQuery,
12
+ linkEndpointObjectTypeIds,
12
13
  normalizeVector,
13
14
  vectorSearchCandidateLimit,
14
15
  } from "@sixb/core/internal/query"
@@ -190,14 +191,7 @@ function compileObjectQueryInternal(
190
191
  case "page":
191
192
  return compilePage(projectId, query.input, query.pageSize, query.pageToken, ctx)
192
193
  case "traverse":
193
- return compileTraversal(
194
- projectId,
195
- query.input,
196
- query.linkId,
197
- query.direction,
198
- query.sourceObjectTypeId,
199
- ctx
200
- )
194
+ return compileTraversal(projectId, query, ctx)
201
195
  case "set":
202
196
  return compileSet(projectId, query.op, query.inputs, ctx)
203
197
  case "project":
@@ -571,14 +565,13 @@ function compilePage(
571
565
 
572
566
  function compileTraversal(
573
567
  projectId: string,
574
- inputQuery: ObjectQuery,
575
- linkId: string,
576
- direction: "outgoing" | "incoming",
577
- sourceObjectTypeId: string | undefined,
568
+ traversal: Extract<ObjectQuery, { kind: "traverse" }>,
578
569
  ctx: CompileContext
579
570
  ): CompiledObjectQuery {
580
- const input = compileObjectQueryInternal(projectId, inputQuery, exactContext(ctx))
571
+ const { linkId, direction, sourceObjectTypeId } = traversal
572
+ const input = compileObjectQueryInternal(projectId, traversal.input, exactContext(ctx))
581
573
  const outputAlias = direction === "outgoing" ? "target_object" : "source_object"
574
+ const endpointType = compileEndpointTypes(outputAlias, linkEndpointObjectTypeIds(traversal))
582
575
  const joinSql =
583
576
  direction === "outgoing"
584
577
  ? `
@@ -591,6 +584,7 @@ function compileTraversal(
591
584
  ON target_object.project_id = edge.project_id
592
585
  AND target_object.object_type_id = edge.target_type_id
593
586
  AND target_object.primary_id = edge.target_id
587
+ ${endpointType.sql}
594
588
  `
595
589
  : `
596
590
  JOIN ${ctx.source.linksTable} AS edge
@@ -602,6 +596,7 @@ function compileTraversal(
602
596
  ON source_object.project_id = edge.project_id
603
597
  AND source_object.object_type_id = edge.source_type_id
604
598
  AND source_object.primary_id = edge.source_id
599
+ ${endpointType.sql}
605
600
  `
606
601
  const order = compileOrder(identityOrderFields())
607
602
  const qualifiedOrder = compileOrder(identityOrderFields(), outputAlias)
@@ -614,7 +609,8 @@ function compileTraversal(
614
609
  const args = [
615
610
  ...input.args,
616
611
  linkId,
617
- ...(sourceObjectTypeId === undefined ? [] : [sourceObjectTypeId]),
612
+ ...(direction === "incoming" && sourceObjectTypeId !== undefined ? [sourceObjectTypeId] : []),
613
+ ...endpointType.args,
618
614
  ...qualifiedOrder.args,
619
615
  ]
620
616
 
@@ -630,6 +626,19 @@ function compileTraversal(
630
626
  }
631
627
  }
632
628
 
629
+ /** Only the types the ontology says this link can reach. */
630
+ function compileEndpointTypes(
631
+ alias: string,
632
+ objectTypeIds: readonly string[] | undefined
633
+ ): CompiledPredicate {
634
+ if (!objectTypeIds?.length) return { sql: "", args: [] }
635
+
636
+ return {
637
+ sql: `AND ${alias}.object_type_id IN (${objectTypeIds.map(() => "?").join(", ")})`,
638
+ args: [...objectTypeIds],
639
+ }
640
+ }
641
+
633
642
  /** Correlation handle for an expansion: the parent row's identity columns. */
634
643
  interface ExpansionParent {
635
644
  project: string
@@ -698,12 +707,12 @@ function compileExpansionsObject(
698
707
  return { sql: `json_object(${parts.join(", ")})`, args }
699
708
  }
700
709
 
701
- // One expansion's hydrated value. `row_number()` numbers a parent's links by the
702
- // order from `compileExpansionOrder` (the expansion's `orderBy` against the target
703
- // object's properties, then an identity tiebreak); `WHERE _ord <= N` keeps the
704
- // top-N per parent in-DB; each retained neighbour becomes a JSON object; and the
705
- // value is an ordered array ("many") or the first element or null ("one"). The
706
- // cardinality is core-resolved before pushdown.
710
+ // One expansion's hydrated value. A parent's links are read in the order from
711
+ // `compileExpansionOrder` (the expansion's `orderBy` against the target object's
712
+ // properties, then an identity tiebreak) and `LIMIT N` keeps the top-N per parent
713
+ // in-DB; each retained neighbour becomes a JSON object; and the value is an
714
+ // ordered array ("many", numbered by `row_number()` for `json_group_array`) or the
715
+ // first neighbour or null ("one"). The cardinality is core-resolved before pushdown.
707
716
  function compileExpansionValue(
708
717
  expansion: ObjectExpansion,
709
718
  parent: ExpansionParent,
@@ -730,27 +739,29 @@ function compileExpansionValue(
730
739
  `${parentId} = ${parent.id}`,
731
740
  `${edge}.link_id = ?`,
732
741
  ]
733
- const whereArgs: SqliteValue[] = [expansion.linkId]
742
+ const endpointType = compileEndpointTypes(target, expansion.endpointObjectTypeIds)
743
+ const whereArgs: SqliteValue[] = [...endpointType.args, expansion.linkId]
734
744
  if (incoming && expansion.sourceObjectTypeId !== undefined) {
735
745
  whereParts.push(`${edge}.source_type_id = ?`)
736
746
  whereArgs.push(expansion.sourceObjectTypeId)
737
747
  }
738
748
 
739
- const inner = `
740
- SELECT ${child.sql} AS elem, row_number() OVER (ORDER BY ${order.sql}) AS _ord
749
+ const from = `
741
750
  FROM ${ctx.source.linksTable} AS ${edge}
742
751
  JOIN ${ctx.source.objectsTable} AS ${target}
743
752
  ON ${target}.project_id = ${edge}.project_id
744
753
  AND ${target}.object_type_id = ${neighborType}
745
754
  AND ${target}.primary_id = ${neighborId}
755
+ ${endpointType.sql}
746
756
  WHERE ${whereParts.join(" AND ")}
747
757
  `
748
- const innerArgs = [...child.args, ...order.args, ...whereArgs]
749
758
 
759
+ // ORDER BY ... LIMIT stops at the kept neighbours, so only they are built as JSON. Filtering
760
+ // on row_number() would build every neighbour first, in a co-routine per parent.
750
761
  if (expansion.cardinality === "one") {
751
762
  return {
752
- sql: `(SELECT ${ranked}.elem FROM (${inner}) AS ${ranked} WHERE ${ranked}._ord = 1)`,
753
- args: innerArgs,
763
+ sql: `(SELECT ${child.sql} ${from} ORDER BY ${order.sql} LIMIT 1)`,
764
+ args: [...child.args, ...whereArgs, ...order.args],
754
765
  }
755
766
  }
756
767
 
@@ -759,8 +770,13 @@ function compileExpansionValue(
759
770
  // rather than the quoted JSON text the subquery column hands back.
760
771
  const limit = expansion.limit ?? DEFAULT_EXPANSION_FANOUT
761
772
  return {
762
- sql: `COALESCE((SELECT json_group_array(json(${ranked}.elem) ORDER BY ${ranked}._ord) FROM (${inner}) AS ${ranked} WHERE ${ranked}._ord <= ?), '[]')`,
763
- args: [...innerArgs, limit],
773
+ sql: `COALESCE((SELECT json_group_array(json(${ranked}.elem) ORDER BY ${ranked}._ord) FROM (
774
+ SELECT ${child.sql} AS elem, row_number() OVER (ORDER BY ${order.sql}) AS _ord
775
+ ${from}
776
+ ORDER BY ${order.sql}
777
+ LIMIT ?
778
+ ) AS ${ranked}), '[]')`,
779
+ args: [...child.args, ...order.args, ...whereArgs, ...order.args, limit],
764
780
  }
765
781
  }
766
782
 
@@ -11,6 +11,7 @@ import type {
11
11
  import {
12
12
  assertNonblank,
13
13
  assertNonnegativeInteger,
14
+ assertTimestamp,
14
15
  commitRecord,
15
16
  originWhere,
16
17
  type SqliteOntologyCommitRow,
@@ -78,6 +79,9 @@ export class SqliteOntologyCommitStorage implements OntologyCommitStorage {
78
79
  const limit = input.limit ?? -1
79
80
  assertNonnegativeInteger(offset, "Ontology commit list offset")
80
81
  if (limit !== -1) assertNonnegativeInteger(limit, "Ontology commit list limit")
82
+ if (input.committedAt !== undefined) {
83
+ assertTimestamp(input.committedAt, "Ontology commit list committedAt")
84
+ }
81
85
  const filters = ["project_id = ?"]
82
86
  const values: (string | number)[] = [input.projectId]
83
87
  if (input.run?.kind === "action") {
@@ -87,6 +91,11 @@ export class SqliteOntologyCommitStorage implements OntologyCommitStorage {
87
91
  filters.push("origin_run_id = ? AND origin_kind IN ('projection', 'telemetry')")
88
92
  values.push(input.run.id)
89
93
  }
94
+ if (input.committedAt !== undefined) {
95
+ // Commit times are stored canonical, so text equality is exact and the index serves it.
96
+ filters.push("committed_at = ?")
97
+ values.push(input.committedAt)
98
+ }
90
99
  const where = filters.join(" AND ")
91
100
  const totalRow = this.db
92
101
  .query(`SELECT COUNT(*) AS total FROM ontology_commits WHERE ${where}`)
@@ -25,6 +25,7 @@ import {
25
25
  invalidCorrelation,
26
26
  materializationOutboxWrite,
27
27
  materializationPlanChunk,
28
+ prepareMaterializationStateRead,
28
29
  sameNonnegativeCounts as sameCounts,
29
30
  uniqueSorted,
30
31
  } from "@sixb/core/internal/ontology-storage-provider"
@@ -36,16 +37,18 @@ import type {
36
37
  MaterializationPlanHeader,
37
38
  MaterializationPlanWorkRecord,
38
39
  MaterializationSession,
40
+ MaterializationState,
39
41
  MaterializationStatePage,
40
42
  MaterializationVectorChangePage,
41
43
  OntologyCommitRecord,
42
44
  OntologyMaterializationStorage,
45
+ ReadMaterializationStateInput,
43
46
  SourceActivationWrite,
44
47
  StageMaterializationWorkInput,
45
48
  StreamMaterializationStateInput,
46
49
  StreamMaterializationVectorChangesInput,
47
50
  } from "@sixb/core/storage"
48
- import { yieldSqliteEventLoop } from "../transactions"
51
+ import { runDeferredReadTransaction, yieldSqliteEventLoop } from "../transactions"
49
52
  import {
50
53
  type SqliteMaterializationSessionState,
51
54
  SqliteMaterializationSessions,
@@ -243,6 +246,20 @@ export class SqliteOntologyMaterializationStorage implements OntologyMaterializa
243
246
  }
244
247
  }
245
248
 
249
+ async readState(input: ReadMaterializationStateInput): Promise<MaterializationState> {
250
+ const request = prepareMaterializationStateRead(input)
251
+
252
+ // One snapshot for every statement, so the read never straddles a commit.
253
+ return runDeferredReadTransaction(this.db, () => {
254
+ const reader = new SqliteMaterializationStateReader(this.db, request.projectId)
255
+ return {
256
+ objects: reader.objectStates(request.objects),
257
+ links: reader.linkStates(request.links),
258
+ linkScopes: reader.linkSlotStates(request.linkScopes),
259
+ }
260
+ })
261
+ }
262
+
246
263
  async stageWork(input: StageMaterializationWorkInput): Promise<void> {
247
264
  this.sessions.stage(input)
248
265
  await yieldSqliteEventLoop()