@sixb/pg 0.1.11 → 0.1.12

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 (46) hide show
  1. package/README.md +34 -0
  2. package/dist/agents/rows.d.ts +3 -1
  3. package/dist/agents/rows.d.ts.map +1 -1
  4. package/dist/agents/threads.d.ts +3 -2
  5. package/dist/agents/threads.d.ts.map +1 -1
  6. package/dist/index.js +553 -54
  7. package/dist/index.js.map +16 -14
  8. package/dist/migrations.d.ts.map +1 -1
  9. package/dist/objects/query-compiler.d.ts +8 -1
  10. package/dist/objects/query-compiler.d.ts.map +1 -1
  11. package/dist/objects/read-scope.d.ts +1 -0
  12. package/dist/objects/read-scope.d.ts.map +1 -1
  13. package/dist/objects/reader.d.ts +1 -0
  14. package/dist/objects/reader.d.ts.map +1 -1
  15. package/dist/objects/rows.d.ts +1 -0
  16. package/dist/objects/rows.d.ts.map +1 -1
  17. package/dist/objects/storage.d.ts +1 -1
  18. package/dist/objects/storage.d.ts.map +1 -1
  19. package/dist/ontology-storage/index.d.ts +4 -0
  20. package/dist/ontology-storage/index.d.ts.map +1 -1
  21. package/dist/ontology-storage/materializations.d.ts +1 -0
  22. package/dist/ontology-storage/materializations.d.ts.map +1 -1
  23. package/dist/ontology-storage/vector-indexing.d.ts +20 -0
  24. package/dist/ontology-storage/vector-indexing.d.ts.map +1 -0
  25. package/dist/ontology-storage/vectors.d.ts +16 -0
  26. package/dist/ontology-storage/vectors.d.ts.map +1 -0
  27. package/dist/pg-ai-cost-storage.d.ts.map +1 -1
  28. package/package.json +3 -3
  29. package/src/agents/rows.ts +21 -0
  30. package/src/agents/threads.ts +47 -2
  31. package/src/migrations/041-agent-thread-workspaces.sql +2 -0
  32. package/src/migrations/042-vector-profiles.sql +28 -0
  33. package/src/migrations/043-vector-indexing.sql +24 -0
  34. package/src/migrations/044-vector-batching.sql +3 -0
  35. package/src/migrations/045-agent-thread-sandbox-state.sql +2 -0
  36. package/src/migrations.ts +14 -0
  37. package/src/objects/query-compiler.ts +102 -11
  38. package/src/objects/read-scope.ts +26 -25
  39. package/src/objects/reader.ts +33 -1
  40. package/src/objects/rows.ts +5 -1
  41. package/src/objects/storage.ts +20 -2
  42. package/src/ontology-storage/index.ts +15 -0
  43. package/src/ontology-storage/materializations.ts +13 -0
  44. package/src/ontology-storage/vector-indexing.ts +168 -0
  45. package/src/ontology-storage/vectors.ts +172 -0
  46. package/src/pg-ai-cost-storage.ts +4 -0
package/dist/index.js CHANGED
@@ -125,6 +125,7 @@ function canStartPgTransaction(sql) {
125
125
  }
126
126
 
127
127
  // src/agents/rows.ts
128
+ import { snapshotAgentThreadSandboxParams } from "@sixb/core/internal/agent-run-storage-provider";
128
129
  import { parseSixbFailure } from "@sixb/core/internal/errors";
129
130
  import {
130
131
  AGENT_RUN_FAILURE_CODES,
@@ -136,6 +137,12 @@ function rowToThreadRecord(row) {
136
137
  projectId: row.project_id,
137
138
  ownerPrincipal: { type: row.owner_principal_type, id: row.owner_principal_id },
138
139
  title: row.title ?? undefined,
140
+ ...row.sandbox_state == null ? {} : {
141
+ sandboxState: typeof row.sandbox_state === "string" ? JSON.parse(row.sandbox_state) : row.sandbox_state
142
+ },
143
+ ...row.sandbox_params == null ? {} : {
144
+ sandboxParams: snapshotAgentThreadSandboxParams(typeof row.sandbox_params === "string" ? JSON.parse(row.sandbox_params) : row.sandbox_params)
145
+ },
139
146
  status: row.status,
140
147
  activeRunId: row.active_run_id,
141
148
  lastMessageAt: row.last_message_at ? new Date(row.last_message_at) : undefined,
@@ -974,6 +981,10 @@ class PgAgentRunStore {
974
981
  }
975
982
 
976
983
  // src/agents/threads.ts
984
+ import {
985
+ snapshotAgentThreadSandboxParams as snapshotAgentThreadSandboxParams2,
986
+ transitionAgentThreadSandbox
987
+ } from "@sixb/core/internal/agent-run-storage-provider";
977
988
  import {
978
989
  AgentStorageError as AgentStorageError4
979
990
  } from "@sixb/core/storage";
@@ -982,7 +993,24 @@ class PgAgentThreadStore {
982
993
  constructor(sql) {
983
994
  this.sql = sql;
984
995
  }
996
+ async transitionSandbox(input) {
997
+ return runPgTransaction(this.sql, async (tx) => {
998
+ const runs = input.action === "recreate" ? [] : await tx`
999
+ SELECT * FROM agent_runs WHERE project_id = ${input.projectId} AND id = ${input.runId}
1000
+ FOR UPDATE
1001
+ `;
1002
+ const [thread] = await tx`
1003
+ SELECT * FROM agent_threads WHERE project_id = ${input.projectId} AND id = ${input.id}
1004
+ FOR UPDATE
1005
+ `;
1006
+ const state = transitionAgentThreadSandbox(thread ? rowToThreadRecord(thread) : null, runs[0] ? rowToRunRecord(runs[0]) : null, input);
1007
+ await tx`UPDATE agent_threads SET sandbox_state = ${JSON.stringify(state)}::text::jsonb
1008
+ WHERE project_id = ${input.projectId} AND id = ${input.id}`;
1009
+ return state;
1010
+ });
1011
+ }
985
1012
  async create(input) {
1013
+ const sandboxParams = input.sandboxParams === undefined ? null : JSON.stringify(snapshotAgentThreadSandboxParams2(input.sandboxParams));
986
1014
  const createdAt = input.createdAt ?? new Date;
987
1015
  const updatedAt = input.updatedAt ?? createdAt;
988
1016
  try {
@@ -993,6 +1021,7 @@ class PgAgentThreadStore {
993
1021
  owner_principal_type,
994
1022
  owner_principal_id,
995
1023
  title,
1024
+ sandbox_params,
996
1025
  status,
997
1026
  created_at,
998
1027
  updated_at
@@ -1002,6 +1031,7 @@ class PgAgentThreadStore {
1002
1031
  ${input.ownerPrincipal.type},
1003
1032
  ${input.ownerPrincipal.id},
1004
1033
  ${input.title ?? null},
1034
+ ${sandboxParams}::text::jsonb,
1005
1035
  ${input.status ?? "active"},
1006
1036
  ${createdAt},
1007
1037
  ${updatedAt}
@@ -7140,6 +7170,80 @@ ALTER TABLE projection_runs ADD COLUMN source_changes_read BIGINT
7140
7170
  CHECK (source_changes_read IS NULL OR source_changes_read >= 0);
7141
7171
  `;
7142
7172
 
7173
+ // src/migrations/041-agent-thread-workspaces.sql
7174
+ var _041_agent_thread_workspaces_default = `ALTER TABLE agent_threads ADD COLUMN sandbox_params JSONB
7175
+ CHECK (sandbox_params IS NULL OR jsonb_typeof(sandbox_params) = 'object');
7176
+ `;
7177
+
7178
+ // src/migrations/042-vector-profiles.sql
7179
+ var _042_vector_profiles_default = `-- Transactional derived state. Search extensions are not required for persistence.
7180
+ CREATE TABLE object_vectors (
7181
+ project_id TEXT NOT NULL,
7182
+ object_type_id TEXT NOT NULL,
7183
+ primary_id TEXT NOT NULL,
7184
+ profile TEXT NOT NULL CHECK (length(btrim(profile)) > 0),
7185
+ configuration TEXT NOT NULL,
7186
+ source TEXT[] NOT NULL,
7187
+ source_fingerprint TEXT NOT NULL,
7188
+ embedding REAL[] NOT NULL,
7189
+ last_commit_id TEXT NOT NULL,
7190
+ PRIMARY KEY (project_id, object_type_id, primary_id, profile),
7191
+ FOREIGN KEY (project_id, object_type_id, primary_id)
7192
+ REFERENCES objects (project_id, object_type_id, primary_id) ON DELETE CASCADE,
7193
+ FOREIGN KEY (project_id, last_commit_id)
7194
+ REFERENCES ontology_commits (project_id, id) DEFERRABLE INITIALLY DEFERRED,
7195
+ CONSTRAINT object_vectors_sources CHECK (
7196
+ cardinality(source) > 0 AND array_ndims(source) = 1 AND array_lower(source, 1) = 1
7197
+ AND array_position(source, NULL) IS NULL
7198
+ ),
7199
+ CONSTRAINT object_vectors_values CHECK (
7200
+ cardinality(embedding) BETWEEN 1 AND 16000
7201
+ AND array_ndims(embedding) = 1 AND array_lower(embedding, 1) = 1
7202
+ AND array_position(embedding, NULL) IS NULL
7203
+ AND '-Infinity'::real < ALL(embedding) AND 'Infinity'::real > ALL(embedding)
7204
+ AND 0::real <> ANY(embedding)
7205
+ )
7206
+ );
7207
+ `;
7208
+
7209
+ // src/migrations/043-vector-indexing.sql
7210
+ var _043_vector_indexing_default = `CREATE TABLE object_vector_indexing (
7211
+ project_id TEXT NOT NULL,
7212
+ id TEXT NOT NULL,
7213
+ object_type_id TEXT NOT NULL,
7214
+ primary_id TEXT NOT NULL,
7215
+ profile TEXT NOT NULL,
7216
+ request JSONB NOT NULL,
7217
+ status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'ready', 'failed')),
7218
+ available_at TIMESTAMPTZ NOT NULL,
7219
+ dispatch_at TIMESTAMPTZ NOT NULL,
7220
+ values_json JSONB,
7221
+ error TEXT,
7222
+ PRIMARY KEY (project_id, id),
7223
+ UNIQUE (project_id, object_type_id, primary_id, profile)
7224
+ );
7225
+ CREATE INDEX object_vector_indexing_due ON object_vector_indexing (project_id, dispatch_at, id)
7226
+ WHERE status <> 'failed';
7227
+
7228
+ ALTER TABLE executions DROP CONSTRAINT executions_authority_kernel_operation_check;
7229
+ ALTER TABLE executions ADD CONSTRAINT executions_authority_kernel_operation_check
7230
+ CHECK (authority_kernel_operation IS NULL OR authority_kernel_operation IN ('ontology.recover', 'ontology.indexVectors'));
7231
+ ALTER TABLE executions DROP CONSTRAINT executions_source_kind_check;
7232
+ ALTER TABLE executions ADD CONSTRAINT executions_source_kind_check
7233
+ CHECK (source_kind IN ('http', 'webhook', 'schedule', 'event', 'datasetVersion', 'execution', 'ontologyCommit'));
7234
+ `;
7235
+
7236
+ // src/migrations/044-vector-batching.sql
7237
+ var _044_vector_batching_default = `ALTER TABLE object_vector_indexing ADD COLUMN batch_id TEXT;
7238
+ CREATE INDEX object_vector_indexing_batch ON object_vector_indexing (project_id, batch_id, id)
7239
+ WHERE batch_id IS NOT NULL;
7240
+ `;
7241
+
7242
+ // src/migrations/045-agent-thread-sandbox-state.sql
7243
+ var _045_agent_thread_sandbox_state_default = `ALTER TABLE agent_threads ADD COLUMN sandbox_state JSONB
7244
+ CHECK (sandbox_state IS NULL OR jsonb_typeof(sandbox_state) = 'object');
7245
+ `;
7246
+
7143
7247
  // src/migrations/037-execution-requester-groups.sql
7144
7248
  var _037_execution_requester_groups_default = `ALTER TABLE executions ADD COLUMN requester_group_ids JSONB NOT NULL DEFAULT '[]'::jsonb;
7145
7249
 
@@ -7392,17 +7496,34 @@ var postgresStorageMigrations = defineMigrations({
7392
7496
  pgSql("037-execution-requester-groups", _037_execution_requester_groups_default),
7393
7497
  pgSql("038-outbox-publication-order", _038_outbox_publication_order_default),
7394
7498
  pgSql("039-projection-source-roots", _039_projection_source_roots_default),
7395
- pgSql("040-connector-optional-pkce", _040_connector_optional_pkce_default)
7499
+ pgSql("040-connector-optional-pkce", _040_connector_optional_pkce_default),
7500
+ pgSql("041-agent-thread-workspaces", _041_agent_thread_workspaces_default),
7501
+ pgSql("042-vector-profiles", _042_vector_profiles_default),
7502
+ pgSql("043-vector-indexing", _043_vector_indexing_default),
7503
+ pgSql("044-vector-batching", _044_vector_batching_default),
7504
+ pgSql("045-agent-thread-sandbox-state", _045_agent_thread_sandbox_state_default)
7396
7505
  ]
7397
7506
  });
7398
7507
 
7508
+ // src/objects/storage.ts
7509
+ import { hasVectorProfile as hasVectorProfile3 } from "@sixb/core/internal/query";
7510
+
7399
7511
  // src/objects/query-compiler.ts
7400
7512
  import {
7401
7513
  ObjectQueryExecutionError
7402
7514
  } from "@sixb/core";
7515
+ import {
7516
+ hasVectorProfile,
7517
+ isVectorProfileQuery,
7518
+ normalizeVector,
7519
+ vectorSearchCandidateLimit
7520
+ } from "@sixb/core/internal/query";
7403
7521
  var PAGE_TOKEN_PREFIX = "keyset:";
7404
7522
  var DEFAULT_EXPANSION_FANOUT = 1000;
7405
7523
  function compilePgObjectQuery(projectId, query, options = {}) {
7524
+ if (hasVectorProfile(query) && !isVectorProfileQuery(query)) {
7525
+ throw new ObjectQueryExecutionError("unsupported_vector_composition", "Vector search supports one profile with filters before ranking and limit/project after.");
7526
+ }
7406
7527
  const source = options.source ?? DEFAULT_OBJECT_QUERY_SOURCE;
7407
7528
  const compiled = source.wrapQuery(compileObjectQueryInternal(projectId, query, {
7408
7529
  probeLimit: options.includeTotal === false,
@@ -7412,6 +7533,7 @@ function compilePgObjectQuery(projectId, query, options = {}) {
7412
7533
  ...compiled,
7413
7534
  sql: numberPlaceholders(compiled.sql),
7414
7535
  totalSql: numberPlaceholders(compiled.totalSql),
7536
+ vectorProbe: compiled.vectorProbe ? { ...compiled.vectorProbe, sql: numberPlaceholders(compiled.vectorProbe.sql) } : undefined,
7415
7537
  hasMoreProbe: compiled.hasMoreProbe ? {
7416
7538
  ...compiled.hasMoreProbe,
7417
7539
  sql: numberPlaceholders(compiled.hasMoreProbe.sql)
@@ -7489,8 +7611,56 @@ function compileObjectQueryInternal(projectId, query, ctx) {
7489
7611
  case "expand":
7490
7612
  return compileExpand(projectId, query.input, query.expansions, ctx);
7491
7613
  case "vector":
7492
- throw new Error(`[SixbPg] PostgreSQL object storage does not support query node '${query.kind}'`);
7493
- }
7614
+ return compileVector(projectId, query, ctx);
7615
+ }
7616
+ }
7617
+ function compileVector(projectId, query, ctx) {
7618
+ if (typeof query.vector === "string" || !isVectorProfileQuery(query) || !query.configuration || !Number.isSafeInteger(query.k) || query.k < 1 || query.k > 1000 || query.vector.length < 1 || query.vector.length > 16000) {
7619
+ throw new ObjectQueryExecutionError("invalid_vector_query", "A validated named vector profile is required.");
7620
+ }
7621
+ const input = compileObjectQueryInternal(projectId, query.input, exactContext(ctx));
7622
+ const permissions = ctx.source.objectPropertyPermissionsTable;
7623
+ const permissionJoin = permissions ? `JOIN (
7624
+ SELECT project_id, object_type_id, primary_id, array_agg(property_id) AS property_ids
7625
+ FROM ${permissions}
7626
+ GROUP BY project_id, object_type_id, primary_id
7627
+ ) AS vector_permissions ON vector_permissions.project_id = vectors.project_id
7628
+ AND vector_permissions.object_type_id = vectors.object_type_id
7629
+ AND vector_permissions.primary_id = vectors.primary_id` : "";
7630
+ const authorized = permissions ? `AND vectors.source <@ vector_permissions.property_ids` : "";
7631
+ const candidates = `FROM (${input.sql}) AS input
7632
+ JOIN object_vectors AS vectors ON vectors.project_id = input.project_id
7633
+ AND vectors.object_type_id = input.object_type_id AND vectors.primary_id = input.primary_id
7634
+ ${permissionJoin}
7635
+ WHERE vectors.profile = ? AND vectors.configuration = ?
7636
+ AND cardinality(vectors.embedding) = ? ${authorized}`;
7637
+ const candidateArgs = [...input.args, query.profile, query.configuration, query.vector.length];
7638
+ const limit = vectorSearchCandidateLimit(query.vector.length);
7639
+ const values = normalizeVector(query.vector, query.vector.length);
7640
+ const order = compileOrder([
7641
+ { kind: "column", column: "_vector_score", direction: "desc" },
7642
+ ...identityOrderFields().map((field) => ({ ...field, binary: true }))
7643
+ ]);
7644
+ const sql = `SELECT * FROM (SELECT input.*, 1 - (vectors.embedding::public.vector OPERATOR(public.<=>) ?::public.vector) AS _vector_score
7645
+ ${candidates}) AS ranked ORDER BY ${order.sql} LIMIT ?`;
7646
+ const args = [JSON.stringify(values), ...candidateArgs, query.k];
7647
+ return {
7648
+ sql,
7649
+ args,
7650
+ totalSql: `SELECT COUNT(*)::bigint AS total FROM (SELECT 1 ${candidates} LIMIT ?) AS eligible`,
7651
+ totalArgs: [...candidateArgs, query.k],
7652
+ order,
7653
+ vectorProbe: {
7654
+ sql: `SELECT COUNT(*)::bigint AS total FROM (SELECT 1 ${candidates} LIMIT ?) AS candidates`,
7655
+ args: [...candidateArgs, limit + 1],
7656
+ limit
7657
+ },
7658
+ hasMore: () => false,
7659
+ trimRows: identityRows,
7660
+ nextPageToken: () => {
7661
+ return;
7662
+ }
7663
+ };
7494
7664
  }
7495
7665
  function compileStart(projectId, query, ctx) {
7496
7666
  if (query.includeSubtypes === true) {
@@ -7578,6 +7748,7 @@ function compileWhere(projectId, inputQuery, predicate, ctx) {
7578
7748
  WHERE ${predicate.sql}
7579
7749
  `,
7580
7750
  totalArgs: [...input.args, ...predicate.args],
7751
+ vectorProbe: input.vectorProbe,
7581
7752
  order: input.order,
7582
7753
  hasMore: () => false,
7583
7754
  trimRows: identityRows,
@@ -7604,7 +7775,7 @@ function compileSort(projectId, inputQuery, fields, ctx) {
7604
7775
  input.created_at,
7605
7776
  input.updated_at,
7606
7777
  input.version,
7607
- input.last_commit_id
7778
+ input.last_commit_id${input.vectorProbe ? ", input._vector_score" : ""}
7608
7779
  FROM (${input.sql}) AS input
7609
7780
  ORDER BY ${order.sql}
7610
7781
  `,
@@ -7635,6 +7806,7 @@ function compileLimit(projectId, inputQuery, rawLimit, ctx) {
7635
7806
  FROM (${input.sql}) AS input
7636
7807
  `,
7637
7808
  totalArgs: input.args,
7809
+ vectorProbe: input.vectorProbe,
7638
7810
  order: input.order,
7639
7811
  hasMore: (rowCount, total) => total === undefined ? ctx.probeLimit && rowCount > limit : limit < total,
7640
7812
  trimRows: ctx.probeLimit ? (rows) => rows.slice(0, limit) : identityRows,
@@ -7663,6 +7835,7 @@ function compilePage(projectId, inputQuery, rawPageSize, pageToken, ctx) {
7663
7835
  FROM (${input.sql}) AS input
7664
7836
  `,
7665
7837
  totalArgs: input.args,
7838
+ vectorProbe: input.vectorProbe,
7666
7839
  order: input.order,
7667
7840
  hasMore: (rowCount) => rowCount > pageSize,
7668
7841
  trimRows: (rows) => rows.slice(0, pageSize),
@@ -7739,6 +7912,7 @@ function compileExpand(projectId, inputQuery, expansions, ctx) {
7739
7912
  args: [...expand.args, ...input.args, ...inputOrder.args],
7740
7913
  totalSql: input.totalSql,
7741
7914
  totalArgs: input.totalArgs,
7915
+ vectorProbe: input.vectorProbe,
7742
7916
  order: input.order,
7743
7917
  hasMoreProbe: input.hasMoreProbe,
7744
7918
  hasMore: input.hasMore,
@@ -7904,13 +8078,14 @@ function compileProject(projectId, inputQuery, properties, ctx) {
7904
8078
  input.created_at,
7905
8079
  input.updated_at,
7906
8080
  input.version,
7907
- input.last_commit_id
8081
+ input.last_commit_id${input.vectorProbe ? ", input._vector_score" : ""}
7908
8082
  FROM (${input.sql}) AS input
7909
8083
  ORDER BY ${inputOrder.sql}
7910
8084
  `,
7911
8085
  args: [...projection.args, ...input.args, ...inputOrder.args],
7912
8086
  totalSql: input.totalSql,
7913
8087
  totalArgs: input.totalArgs,
8088
+ vectorProbe: input.vectorProbe,
7914
8089
  order: outputOrder,
7915
8090
  hasMoreProbe: input.hasMoreProbe,
7916
8091
  hasMore: input.hasMore,
@@ -7941,7 +8116,7 @@ function compileAggregateSource(projectId, query, source) {
7941
8116
  case "page":
7942
8117
  return compileRowQueryAggregateSource(projectId, query, source);
7943
8118
  case "vector":
7944
- throw new Error(`[SixbPg] PostgreSQL object storage does not support query node '${query.kind}'`);
8119
+ return compileRowQueryAggregateSource(projectId, query, source);
7945
8120
  }
7946
8121
  }
7947
8122
  function compileAggregateStart(projectId, query, source) {
@@ -8310,7 +8485,7 @@ function compileOrder(fields, qualifier, propertyColumn = "properties") {
8310
8485
  const args = [];
8311
8486
  for (const field of fields) {
8312
8487
  if (field.kind === "column") {
8313
- clauses.push(`${columnExpression(field.column, qualifier)} ${field.direction.toUpperCase()}`);
8488
+ clauses.push(`${columnExpression(field.column, qualifier)}${field.binary ? ' COLLATE "C"' : ""} ${field.direction.toUpperCase()}`);
8314
8489
  continue;
8315
8490
  }
8316
8491
  const direction = field.direction === "desc" ? "DESC" : "ASC";
@@ -8519,6 +8694,8 @@ function isPlainObject(value) {
8519
8694
  }
8520
8695
 
8521
8696
  // src/objects/reader.ts
8697
+ import { ObjectQueryExecutionError as ObjectQueryExecutionError2 } from "@sixb/core";
8698
+ import { hasVectorProfile as hasVectorProfile2 } from "@sixb/core/internal/query";
8522
8699
  import { linkBatchKey, objectBatchKey } from "@sixb/core/storage";
8523
8700
 
8524
8701
  // src/objects/rows.ts
@@ -8535,7 +8712,10 @@ function rowToObject(row) {
8535
8712
  };
8536
8713
  }
8537
8714
  function queryRowToObject(row) {
8538
- const base = rowToObject(row);
8715
+ const base = {
8716
+ ...rowToObject(row),
8717
+ ...row._vector_score === undefined ? {} : { score: row._vector_score }
8718
+ };
8539
8719
  const links = reviveExpandedLinks(row._expand);
8540
8720
  return links ? { ...base, links } : base;
8541
8721
  }
@@ -8610,7 +8790,23 @@ class PgObjectReader {
8610
8790
  this.sql = sql;
8611
8791
  this.source = source;
8612
8792
  }
8793
+ async assertVectorSearch(params) {
8794
+ if (!hasVectorProfile2(params.query))
8795
+ return;
8796
+ const compiled = compilePgObjectQuery(params.projectId, params.query, { source: this.source });
8797
+ if (!compiled.vectorProbe)
8798
+ return;
8799
+ const extensions = await this.sql.unsafe("SELECT EXISTS (SELECT 1 FROM pg_extension e JOIN pg_namespace n ON n.oid = e.extnamespace WHERE e.extname = 'vector' AND n.nspname = 'public') AS ready");
8800
+ if (!extensions[0]?.ready)
8801
+ throw new ObjectQueryExecutionError2("vector_extension_missing", "PostgreSQL vector search requires pgvector installed in schema public (CREATE EXTENSION vector WITH SCHEMA public).");
8802
+ const probe = compiled.vectorProbe;
8803
+ const [row] = await this.sql.unsafe(probe.sql, probe.args);
8804
+ if (Number(row?.total ?? 0) > probe.limit) {
8805
+ throw new ObjectQueryExecutionError2("vector_work_limit_exceeded", `Exact vector search supports at most ${probe.limit} eligible vectors at this dimension. Narrow the query filters.`);
8806
+ }
8807
+ }
8613
8808
  async queryObjects(params) {
8809
+ await this.assertVectorSearch(params);
8614
8810
  const sql = this.sql;
8615
8811
  const source = this.source;
8616
8812
  const compiled = compilePgObjectQuery(params.projectId, params.query, {
@@ -8629,6 +8825,7 @@ class PgObjectReader {
8629
8825
  };
8630
8826
  }
8631
8827
  async countObjects(params) {
8828
+ await this.assertVectorSearch(params);
8632
8829
  const sql = this.sql;
8633
8830
  const source = this.source;
8634
8831
  const compiled = compilePgObjectCountQuery(params.projectId, stripOuterRowShape(params.query), {
@@ -8638,6 +8835,7 @@ class PgObjectReader {
8638
8835
  return { count: Number(row?.count ?? 0) };
8639
8836
  }
8640
8837
  async existsObjects(params) {
8838
+ await this.assertVectorSearch(params);
8641
8839
  const sql = this.sql;
8642
8840
  const source = this.source;
8643
8841
  const compiled = compilePgObjectExistsQuery(params.projectId, stripOuterRowShape(params.query), { source });
@@ -8645,6 +8843,7 @@ class PgObjectReader {
8645
8843
  return { exists: row !== undefined };
8646
8844
  }
8647
8845
  async facetObjects(params) {
8846
+ await this.assertVectorSearch(params);
8648
8847
  const sql = this.sql;
8649
8848
  const source = this.source;
8650
8849
  const facets = [];
@@ -9032,6 +9231,7 @@ ${sql}`,
9032
9231
  wrapQuery: (query) => ({
9033
9232
  ...query,
9034
9233
  ...wrapStatement(query.sql, query.args),
9234
+ vectorProbe: query.vectorProbe ? { ...query.vectorProbe, ...wrapStatement(query.vectorProbe.sql, query.vectorProbe.args) } : undefined,
9035
9235
  totalSql: `${compiled.sql}
9036
9236
  ${query.totalSql}`,
9037
9237
  totalArgs: [...compiled.args, ...query.totalArgs],
@@ -9153,32 +9353,29 @@ function compileSelectedReadScopeCte(scopeJson, projectId) {
9153
9353
  SELECT DISTINCT project_id, object_type_id, primary_id
9154
9354
  FROM _sixb_scope_reachable
9155
9355
  ),
9356
+ _sixb_scope_object_grants AS (
9357
+ SELECT reachable.project_id, reachable.object_type_id, reachable.primary_id,
9358
+ array_agg(DISTINCT selected_property.value) FILTER (WHERE selected_property.value IS NOT NULL) AS property_ids
9359
+ FROM _sixb_scope_reachable AS reachable
9360
+ LEFT JOIN _sixb_scope_object_selections AS selection
9361
+ ON selection.node_id = reachable.node_id
9362
+ AND selection.object_type_id = reachable.object_type_id
9363
+ LEFT JOIN LATERAL jsonb_array_elements_text(selection.property_ids) AS selected_property(value) ON true
9364
+ GROUP BY reachable.project_id, reachable.object_type_id, reachable.primary_id
9365
+ ),
9156
9366
  _sixb_scope_objects AS (
9157
- SELECT
9158
- stored.project_id,
9159
- stored.object_type_id,
9160
- stored.primary_id,
9161
- COALESCE(
9162
- (
9163
- SELECT jsonb_object_agg(property.key, property.value)
9164
- FROM jsonb_each(stored.properties) AS property(key, value)
9165
- JOIN _sixb_scope_object_permissions AS permission
9166
- ON permission.project_id = stored.project_id
9167
- AND permission.object_type_id = stored.object_type_id
9168
- AND permission.primary_id = stored.primary_id
9169
- AND permission.property_id = property.key
9170
- ),
9171
- '{}'::jsonb
9172
- ) AS properties,
9173
- stored.created_at,
9174
- stored.updated_at,
9175
- stored.version,
9176
- stored.last_commit_id
9177
- FROM objects AS stored
9178
- JOIN _sixb_scope_object_ids AS visible
9179
- ON visible.project_id = stored.project_id
9180
- AND visible.object_type_id = stored.object_type_id
9181
- AND visible.primary_id = stored.primary_id
9367
+ SELECT stored.project_id, stored.object_type_id, stored.primary_id,
9368
+ COALESCE((
9369
+ SELECT jsonb_object_agg(property.key, property.value)
9370
+ FROM jsonb_each(stored.properties) AS property(key, value)
9371
+ WHERE property.key = ANY(grants.property_ids)
9372
+ ), '{}'::jsonb) AS properties,
9373
+ stored.created_at, stored.updated_at, stored.version, stored.last_commit_id
9374
+ FROM _sixb_scope_object_grants AS grants
9375
+ JOIN objects AS stored
9376
+ ON stored.project_id = grants.project_id
9377
+ AND stored.object_type_id = grants.object_type_id
9378
+ AND stored.primary_id = grants.primary_id
9182
9379
  ),
9183
9380
  _sixb_scope_reached_links(
9184
9381
  project_id,
@@ -9385,6 +9582,7 @@ var PG_OBJECT_QUERY_CAPABILITIES = {
9385
9582
  refs: true,
9386
9583
  filter: true,
9387
9584
  text: true,
9585
+ vector: true,
9388
9586
  sort: true,
9389
9587
  limit: true,
9390
9588
  page: true,
@@ -9437,7 +9635,7 @@ var PG_OBJECT_QUERY_CAPABILITIES = {
9437
9635
  "PostgreSQL object query pushdown supports start/refs/filter/text/sort/limit/page/traverse/set/project/expand over JSONB properties and object links.",
9438
9636
  "expand hydrates linked objects in-database (top-N per parent via LATERAL + jsonb_agg); core resolves each expansion's cardinality before pushdown, and a mixed/unresolved one stays on the fallback.",
9439
9637
  "Exact decimal predicates, ordering, and keyset pagination use PostgreSQL numeric casts.",
9440
- "Relevance sorting, vector search, and unresolved start.includeSubtypes remain planner fallback or rejection cases."
9638
+ "Relevance sorting and unresolved start.includeSubtypes remain planner fallback or rejection cases."
9441
9639
  ]
9442
9640
  };
9443
9641
 
@@ -9455,15 +9653,23 @@ class PgObjectStorage {
9455
9653
  return createPgSelectedReader(this.sql, params, () => this.queryCapabilities());
9456
9654
  }
9457
9655
  async queryObjects(params) {
9656
+ if (hasVectorProfile3(params.query))
9657
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).queryObjects(params));
9458
9658
  return this.reader.queryObjects(params);
9459
9659
  }
9460
9660
  async countObjects(params) {
9661
+ if (hasVectorProfile3(params.query))
9662
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).countObjects(params));
9461
9663
  return this.reader.countObjects(params);
9462
9664
  }
9463
9665
  async existsObjects(params) {
9666
+ if (hasVectorProfile3(params.query))
9667
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).existsObjects(params));
9464
9668
  return this.reader.existsObjects(params);
9465
9669
  }
9466
9670
  async facetObjects(params) {
9671
+ if (hasVectorProfile3(params.query))
9672
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).facetObjects(params));
9467
9673
  return this.reader.facetObjects(params);
9468
9674
  }
9469
9675
  async getByPrimaryId(params) {
@@ -9766,6 +9972,7 @@ import {
9766
9972
  linkRefSortKey,
9767
9973
  linkScopeSortKey as linkScopeSortKey2,
9768
9974
  MaterializationConflictError as MaterializationConflictError6,
9975
+ MaterializationValidationError as MaterializationValidationError2,
9769
9976
  objectRefKey as objectRefKey3,
9770
9977
  objectRefSortKey,
9771
9978
  telemetryPointKey as telemetryPointKey3,
@@ -11692,6 +11899,12 @@ class PgOntologyMaterializationStorage {
11692
11899
  this.sessions = new PgMaterializationSessions(sql, context);
11693
11900
  this.writer = new PgMaterializationWriter(sql);
11694
11901
  }
11902
+ assertVectorSession(session, projectId, commitId) {
11903
+ const active = this.sessions.require(session);
11904
+ if (active.header.commit.projectId !== projectId || commitId !== undefined && active.header.commit.id !== commitId) {
11905
+ throw new MaterializationValidationError2("Vector mutation does not belong to this materialization session.");
11906
+ }
11907
+ }
11695
11908
  async begin(input) {
11696
11909
  assertMaterializationHeader(input);
11697
11910
  const session = await this.sessions.create(input);
@@ -12299,7 +12512,7 @@ function databaseCount(value) {
12299
12512
  import { serializeSixbFailure as serializeSixbFailure2 } from "@sixb/core/internal/errors";
12300
12513
  import {
12301
12514
  MaterializationConflictError as MaterializationConflictError7,
12302
- MaterializationValidationError as MaterializationValidationError2
12515
+ MaterializationValidationError as MaterializationValidationError3
12303
12516
  } from "@sixb/core/internal/materialization";
12304
12517
  import { ONTOLOGY_OUTBOX_FAILURE_CODES as ONTOLOGY_OUTBOX_FAILURE_CODES2 } from "@sixb/core/storage";
12305
12518
  class PgOntologyOutboxStorage {
@@ -12315,7 +12528,7 @@ class PgOntologyOutboxStorage {
12315
12528
  const leaseExpiry = assertTimestamp(input.leaseExpiresAt, "Ontology outbox lease expiry");
12316
12529
  assertNonblank(input.leaseId, "Ontology outbox lease id");
12317
12530
  if (leaseExpiry <= now) {
12318
- throw new MaterializationValidationError2("Ontology outbox lease expiry must be later than the claim time.");
12531
+ throw new MaterializationValidationError3("Ontology outbox lease expiry must be later than the claim time.");
12319
12532
  }
12320
12533
  const rows = await sql`
12321
12534
  WITH candidates AS (
@@ -12466,7 +12679,7 @@ function validateLeaseIds(ids) {
12466
12679
  for (const id of ids) {
12467
12680
  assertNonblank(id, "Ontology outbox event id");
12468
12681
  if (seen.has(id)) {
12469
- throw new MaterializationValidationError2(`Ontology outbox lease batch repeats event '${id}'.`);
12682
+ throw new MaterializationValidationError3(`Ontology outbox lease batch repeats event '${id}'.`);
12470
12683
  }
12471
12684
  seen.add(id);
12472
12685
  }
@@ -12481,7 +12694,7 @@ function leaseConflict() {
12481
12694
  }
12482
12695
 
12483
12696
  // src/ontology-storage/sources.ts
12484
- import { MaterializationValidationError as MaterializationValidationError3 } from "@sixb/core/internal/materialization";
12697
+ import { MaterializationValidationError as MaterializationValidationError4 } from "@sixb/core/internal/materialization";
12485
12698
  import {
12486
12699
  assertSourceBeginInput as assertBeginInput,
12487
12700
  assertSourceExecutionIdentity as assertExecutionIdentity,
@@ -12641,7 +12854,7 @@ class PgOntologySourceStorage {
12641
12854
  throw sourceConflict(`Source materialization '${input.materializationId}' cannot transition from '${manifest.status}' to 'ready'.`);
12642
12855
  }
12643
12856
  if (input.readyAt < toIsoString2(manifest.created_at)) {
12644
- throw new MaterializationValidationError3("Source readyAt cannot precede source createdAt.");
12857
+ throw new MaterializationValidationError4("Source readyAt cannot precede source createdAt.");
12645
12858
  }
12646
12859
  if (input.assertionCount >= 1000 || input.rootCount >= 1000) {
12647
12860
  await sql`ANALYZE ontology_source_rows, ontology_source_roots`;
@@ -12739,7 +12952,7 @@ class PgOntologySourceStorage {
12739
12952
  }
12740
12953
  async transitionToAbandoned(sql, manifest, abandonedAt) {
12741
12954
  if (abandonedAt < toIsoString2(manifest.created_at) || manifest.ready_at !== null && abandonedAt < toIsoString2(manifest.ready_at)) {
12742
- throw new MaterializationValidationError3("Source abandonedAt cannot precede source creation or readiness.");
12955
+ throw new MaterializationValidationError4("Source abandonedAt cannot precede source creation or readiness.");
12743
12956
  }
12744
12957
  const rows = await sql`
12745
12958
  UPDATE ontology_sources
@@ -12777,7 +12990,7 @@ class PgOntologySourceStorage {
12777
12990
  const minimum = counts?.min_ordinal === null ? null : Number(counts?.min_ordinal);
12778
12991
  const maximum = counts?.max_ordinal === null ? null : Number(counts?.max_ordinal);
12779
12992
  if (assertions !== assertionCount || roots !== rootCount || ordinals !== rootCount || rootCount > 0 && (minimum !== 0 || maximum !== rootCount - 1)) {
12780
- throw new MaterializationValidationError3("Source ready counts do not match the staged roots and assertions.");
12993
+ throw new MaterializationValidationError4("Source ready counts do not match the staged roots and assertions.");
12781
12994
  }
12782
12995
  await assertSourceRootCoverage(sql, manifest);
12783
12996
  const [invalid] = manifest.projection_kind === "link" ? await sql`
@@ -12813,7 +13026,7 @@ class PgOntologySourceStorage {
12813
13026
  LIMIT 1
12814
13027
  `;
12815
13028
  if (invalid) {
12816
- throw new MaterializationValidationError3(manifest.projection_kind === "link" ? "Link projection roots must contain exactly their matching link assertion." : "Object projection roots must contain exactly their matching object assertion plus links sourced from that root.");
13029
+ throw new MaterializationValidationError4(manifest.projection_kind === "link" ? "Link projection roots must contain exactly their matching link assertion." : "Object projection roots must contain exactly their matching object assertion plus links sourced from that root.");
12817
13030
  }
12818
13031
  }
12819
13032
  async findStageRows(sql, input, rows) {
@@ -12945,17 +13158,300 @@ function numberOrNull(value) {
12945
13158
  return value === null ? null : Number(value);
12946
13159
  }
12947
13160
 
13161
+ // src/ontology-storage/vector-indexing.ts
13162
+ import { parseSixbFailure as parseSixbFailure3 } from "@sixb/core/internal/errors";
13163
+ import {
13164
+ VECTOR_INDEXING_FAILURE_CODES
13165
+ } from "@sixb/core/storage";
13166
+ class PgVectorIndexingStorage {
13167
+ sql;
13168
+ run;
13169
+ assertSession;
13170
+ constructor(sql, run, assertSession) {
13171
+ this.sql = sql;
13172
+ this.run = run;
13173
+ this.assertSession = assertSession;
13174
+ }
13175
+ async schedule(input) {
13176
+ this.assertSession(input.session, input.projectId);
13177
+ if (input.deleted.length)
13178
+ await this.sql`
13179
+ DELETE FROM object_vector_indexing AS work
13180
+ USING jsonb_array_elements(${jsonParameter2(this.sql, input.deleted)}::jsonb) AS deleted(ref)
13181
+ WHERE work.project_id = ${input.projectId}
13182
+ AND work.object_type_id = deleted.ref->>'objectTypeId'
13183
+ AND work.primary_id = deleted.ref->>'primaryId'
13184
+ `;
13185
+ if (input.requests.length)
13186
+ await this.sql`
13187
+ INSERT INTO object_vector_indexing (
13188
+ project_id, id, object_type_id, primary_id, profile, batch_id, request, status, available_at, dispatch_at
13189
+ )
13190
+ SELECT ${input.projectId}, entry->>'id', entry#>>'{ref,objectTypeId}',
13191
+ entry#>>'{ref,primaryId}', entry->>'profile', entry->>'batchId', entry - 'batchId', 'pending',
13192
+ ${input.availableAt}::timestamptz, ${input.availableAt}::timestamptz
13193
+ FROM jsonb_array_elements(${jsonParameter2(this.sql, input.requests)}::jsonb) AS pending(entry)
13194
+ ON CONFLICT (project_id, object_type_id, primary_id, profile) DO UPDATE SET
13195
+ id = EXCLUDED.id, batch_id = EXCLUDED.batch_id, request = EXCLUDED.request, status = 'pending',
13196
+ available_at = EXCLUDED.available_at, dispatch_at = EXCLUDED.dispatch_at,
13197
+ values_json = NULL, error = NULL
13198
+ `;
13199
+ }
13200
+ async complete(input) {
13201
+ this.assertSession(input.session, input.projectId);
13202
+ if (!input.entries.length)
13203
+ return;
13204
+ await this.sql`
13205
+ DELETE FROM object_vector_indexing AS work
13206
+ USING jsonb_array_elements(${jsonParameter2(this.sql, input.entries)}::jsonb) AS completed(entry)
13207
+ WHERE work.project_id = ${input.projectId}
13208
+ AND work.object_type_id = entry#>>'{ref,objectTypeId}'
13209
+ AND work.primary_id = entry#>>'{ref,primaryId}'
13210
+ AND work.profile = entry->>'profile'
13211
+ AND work.request->>'configuration' = entry->>'configuration'
13212
+ AND work.request->>'sourceFingerprint' = entry->>'sourceFingerprint'
13213
+ `;
13214
+ }
13215
+ async dispatched(input) {
13216
+ if (!input.ids.length)
13217
+ return;
13218
+ await this.run(async (sql) => {
13219
+ await sql`UPDATE object_vector_indexing SET dispatch_at = ${input.nextDispatchAt} WHERE project_id = ${input.projectId} AND id = ANY(${sql.array([...input.ids])}::text[])`;
13220
+ });
13221
+ }
13222
+ async get(input) {
13223
+ return this.run(async (sql) => {
13224
+ const rows = await sql`SELECT batch_id, request, status, available_at, values_json, error FROM object_vector_indexing WHERE project_id = ${input.projectId} AND id = ${input.id}`;
13225
+ return rows[0] ? decode(rows[0]) : null;
13226
+ });
13227
+ }
13228
+ async getBatch(input) {
13229
+ return this.run(async (sql) => (await sql`
13230
+ SELECT batch_id, request, status, available_at, values_json, error
13231
+ FROM object_vector_indexing WHERE project_id = ${input.projectId}
13232
+ AND batch_id = ${input.batchId} ORDER BY id`).map(decode));
13233
+ }
13234
+ async getBatchMember(input) {
13235
+ return this.run(async (sql) => {
13236
+ const rows = await sql`
13237
+ SELECT batch_id, request, status, available_at, values_json, error
13238
+ FROM object_vector_indexing WHERE project_id = ${input.projectId}
13239
+ AND object_type_id = ${input.ref.objectTypeId} AND primary_id = ${input.ref.primaryId}
13240
+ AND profile = ${input.profile} AND batch_id = ${input.batchId}`;
13241
+ return rows[0] ? decode(rows[0]) : null;
13242
+ });
13243
+ }
13244
+ async updateBatch(input) {
13245
+ if (!input.updates.length)
13246
+ return true;
13247
+ return this.run(async (sql) => {
13248
+ const rows = await sql`
13249
+ WITH changes AS MATERIALIZED (
13250
+ SELECT item FROM jsonb_array_elements(${jsonParameter2(sql, input.updates)}::jsonb) AS entries(item)
13251
+ ), locked AS MATERIALIZED (
13252
+ SELECT work.id FROM object_vector_indexing AS work
13253
+ JOIN changes ON work.id = item->>'id' AND work.status = item->>'expectedStatus'
13254
+ WHERE work.project_id = ${input.projectId} ORDER BY work.id FOR UPDATE OF work
13255
+ )
13256
+ UPDATE object_vector_indexing AS work SET
13257
+ status = changes.item->>'status', available_at = (changes.item->>'availableAt')::timestamptz,
13258
+ values_json = COALESCE(changes.item->'values', work.values_json),
13259
+ error = (changes.item->'error')::text
13260
+ FROM changes, locked
13261
+ WHERE work.project_id = ${input.projectId} AND work.id = locked.id
13262
+ AND work.id = changes.item->>'id'
13263
+ AND (${!input.requireAll} OR (SELECT count(*) FROM locked) = ${input.updates.length})
13264
+ RETURNING work.id`;
13265
+ return !input.requireAll || rows.length === input.updates.length;
13266
+ });
13267
+ }
13268
+ async listDue(input) {
13269
+ return this.run(async (sql) => (await sql`SELECT batch_id, request, status, available_at, NULL AS values_json, error FROM object_vector_indexing WHERE project_id = ${input.projectId} AND status <> 'failed' AND dispatch_at <= ${input.now} ORDER BY dispatch_at, id LIMIT ${input.limit}`).map(decode));
13270
+ }
13271
+ async update(input) {
13272
+ return this.run(async (sql) => (await sql`
13273
+ UPDATE object_vector_indexing SET status = ${input.status}, available_at = ${input.availableAt},
13274
+ values_json = COALESCE(${input.values ? jsonParameter2(sql, input.values) : null}, values_json), error = ${input.error ? JSON.stringify(input.error) : null}
13275
+ WHERE project_id = ${input.projectId} AND id = ${input.id} AND status = ${input.expectedStatus} RETURNING id
13276
+ `).length === 1);
13277
+ }
13278
+ async remove(input) {
13279
+ await this.run(async (sql) => {
13280
+ await sql`DELETE FROM object_vector_indexing WHERE project_id = ${input.projectId} AND id = ${input.id}`;
13281
+ });
13282
+ }
13283
+ }
13284
+ function decode(row) {
13285
+ return {
13286
+ ...row.request,
13287
+ ...row.batch_id ? { batchId: row.batch_id } : {},
13288
+ status: row.status,
13289
+ availableAt: new Date(row.available_at).toISOString(),
13290
+ ...row.values_json ? { values: row.values_json } : {},
13291
+ ...row.error ? {
13292
+ error: parseSixbFailure3(row.error, VECTOR_INDEXING_FAILURE_CODES)
13293
+ } : {}
13294
+ };
13295
+ }
13296
+
13297
+ // src/ontology-storage/vectors.ts
13298
+ import { MaterializationConflictError as MaterializationConflictError8 } from "@sixb/core/internal/materialization";
13299
+ class PgOntologyVectorStorage {
13300
+ sql;
13301
+ runRootOperation;
13302
+ assertSession;
13303
+ constructor(sql, runRootOperation, assertSession) {
13304
+ this.sql = sql;
13305
+ this.runRootOperation = runRootOperation;
13306
+ this.assertSession = assertSession;
13307
+ }
13308
+ async list(input) {
13309
+ return this.runRootOperation(async (sql) => {
13310
+ const rows = await sql`
13311
+ SELECT profile, configuration, source, source_fingerprint, last_commit_id
13312
+ FROM object_vectors
13313
+ WHERE project_id = ${input.projectId}
13314
+ AND object_type_id = ${input.ref.objectTypeId} AND primary_id = ${input.ref.primaryId}
13315
+ ORDER BY profile
13316
+ `;
13317
+ return rows.map((row) => ({
13318
+ ref: { ...input.ref },
13319
+ profile: row.profile,
13320
+ configuration: row.configuration,
13321
+ source: row.source,
13322
+ sourceFingerprint: row.source_fingerprint,
13323
+ lastCommitId: row.last_commit_id
13324
+ }));
13325
+ });
13326
+ }
13327
+ async listBatch(input) {
13328
+ if (!input.refs.length)
13329
+ return [];
13330
+ return this.runRootOperation(async (sql) => {
13331
+ const rows = await sql`
13332
+ SELECT stored.object_type_id, stored.primary_id, stored.profile, stored.configuration,
13333
+ stored.source, stored.source_fingerprint, stored.last_commit_id
13334
+ FROM (SELECT DISTINCT ref->>'objectTypeId' AS object_type_id, ref->>'primaryId' AS primary_id
13335
+ FROM jsonb_array_elements(${jsonParameter2(sql, input.refs)}::jsonb) AS requested(ref)) AS requested
13336
+ JOIN object_vectors AS stored ON stored.project_id = ${input.projectId}
13337
+ AND stored.object_type_id = requested.object_type_id AND stored.primary_id = requested.primary_id
13338
+ ORDER BY stored.object_type_id, stored.primary_id, stored.profile
13339
+ `;
13340
+ return rows.map((row) => ({
13341
+ ref: { objectTypeId: row.object_type_id, primaryId: row.primary_id },
13342
+ profile: row.profile,
13343
+ configuration: row.configuration,
13344
+ source: row.source,
13345
+ sourceFingerprint: row.source_fingerprint,
13346
+ lastCommitId: row.last_commit_id
13347
+ }));
13348
+ });
13349
+ }
13350
+ async removeBatch(input) {
13351
+ this.assertSession(input.session, input.projectId);
13352
+ if (!input.entries.length)
13353
+ return;
13354
+ const removed = await this.sql`
13355
+ DELETE FROM object_vectors AS stored
13356
+ USING jsonb_array_elements(${jsonParameter2(this.sql, input.entries)}::jsonb) AS requested(entry)
13357
+ WHERE stored.project_id = ${input.projectId}
13358
+ AND stored.object_type_id = entry#>>'{ref,objectTypeId}'
13359
+ AND stored.primary_id = entry#>>'{ref,primaryId}' AND stored.profile = entry->>'profile'
13360
+ AND stored.last_commit_id = entry->>'expectedCommitId'
13361
+ RETURNING stored.profile
13362
+ `;
13363
+ if (removed.length !== input.entries.length)
13364
+ throw vectorConflict();
13365
+ }
13366
+ async write(input) {
13367
+ await this.writeBatch({ session: input.session, projectId: input.projectId, entries: [input] });
13368
+ }
13369
+ async writeBatch(input) {
13370
+ const entries = input.entries.map(({ value, expectedCommitId }) => {
13371
+ this.assertSession(input.session, input.projectId, value.lastCommitId);
13372
+ return {
13373
+ object_type_id: value.ref.objectTypeId,
13374
+ primary_id: value.ref.primaryId,
13375
+ profile: value.profile,
13376
+ configuration: value.configuration,
13377
+ source: [...value.source],
13378
+ source_fingerprint: value.sourceFingerprint,
13379
+ embedding: [...value.values],
13380
+ last_commit_id: value.lastCommitId,
13381
+ expected_commit_id: expectedCommitId
13382
+ };
13383
+ });
13384
+ const inserts = entries.filter((entry) => entry.expected_commit_id === null);
13385
+ const updates = entries.filter((entry) => entry.expected_commit_id !== null);
13386
+ if (inserts.length) {
13387
+ const rows = await this.sql`
13388
+ INSERT INTO object_vectors (
13389
+ project_id, object_type_id, primary_id, profile, configuration,
13390
+ source, source_fingerprint, embedding, last_commit_id
13391
+ )
13392
+ SELECT ${input.projectId}, object_type_id, primary_id, profile, configuration,
13393
+ source, source_fingerprint, embedding, last_commit_id
13394
+ FROM jsonb_to_recordset(${jsonParameter2(this.sql, inserts)}::jsonb) AS incoming(
13395
+ object_type_id text, primary_id text, profile text, configuration text,
13396
+ source text[], source_fingerprint text, embedding real[], last_commit_id text
13397
+ )
13398
+ ORDER BY object_type_id, primary_id, profile
13399
+ ON CONFLICT (project_id, object_type_id, primary_id, profile) DO NOTHING
13400
+ RETURNING profile`;
13401
+ if (rows.length !== inserts.length)
13402
+ throw vectorConflict();
13403
+ }
13404
+ if (updates.length) {
13405
+ const rows = await this.sql`
13406
+ UPDATE object_vectors AS stored SET
13407
+ configuration = incoming.configuration, source = incoming.source,
13408
+ source_fingerprint = incoming.source_fingerprint, embedding = incoming.embedding,
13409
+ last_commit_id = incoming.last_commit_id
13410
+ FROM jsonb_to_recordset(${jsonParameter2(this.sql, updates)}::jsonb) AS incoming(
13411
+ object_type_id text, primary_id text, profile text, configuration text,
13412
+ source text[], source_fingerprint text, embedding real[], last_commit_id text,
13413
+ expected_commit_id text
13414
+ )
13415
+ WHERE stored.project_id = ${input.projectId}
13416
+ AND stored.object_type_id = incoming.object_type_id AND stored.primary_id = incoming.primary_id
13417
+ AND stored.profile = incoming.profile AND stored.last_commit_id = incoming.expected_commit_id
13418
+ RETURNING stored.profile`;
13419
+ if (rows.length !== updates.length)
13420
+ throw vectorConflict();
13421
+ }
13422
+ }
13423
+ async remove(input) {
13424
+ this.assertSession(input.session, input.projectId);
13425
+ const rows = await this.sql`
13426
+ DELETE FROM object_vectors
13427
+ WHERE project_id = ${input.projectId}
13428
+ AND object_type_id = ${input.ref.objectTypeId} AND primary_id = ${input.ref.primaryId}
13429
+ AND profile = ${input.profile} AND last_commit_id = ${input.expectedCommitId}
13430
+ RETURNING profile
13431
+ `;
13432
+ if (rows.length !== 1)
13433
+ throw vectorConflict();
13434
+ }
13435
+ }
13436
+ function vectorConflict() {
13437
+ return new MaterializationConflictError8("effective-state", "Vector changed since it was prepared.");
13438
+ }
13439
+
12948
13440
  // src/ontology-storage/index.ts
12949
13441
  class PgOntologyStorage {
12950
13442
  commits;
12951
13443
  sources;
12952
13444
  materializations;
12953
13445
  outbox;
13446
+ vectorIndexing;
13447
+ vectors;
12954
13448
  constructor(input) {
12955
13449
  this.commits = new PgOntologyCommitStorage(input.runRootOperation);
12956
13450
  this.sources = new PgOntologySourceStorage(input.runRootOperation);
12957
13451
  this.materializations = new PgOntologyMaterializationStorage(input.sql, input.transactionContext);
12958
13452
  this.outbox = new PgOntologyOutboxStorage(input.runRootOperation);
13453
+ this.vectorIndexing = new PgVectorIndexingStorage(input.sql, input.runRootOperation, (session, projectId) => this.materializations.assertVectorSession(session, projectId));
13454
+ this.vectors = new PgOntologyVectorStorage(input.sql, input.runRootOperation, (session, projectId, commitId) => this.materializations.assertVectorSession(session, projectId, commitId));
12959
13455
  }
12960
13456
  deactivateSessions() {
12961
13457
  this.materializations.deactivateSessions();
@@ -13925,6 +14421,8 @@ class PgAiCostStorage {
13925
14421
  COUNT(*)::text AS model_call_count,
13926
14422
  COUNT(*) FILTER (WHERE reporting_status <> 'unavailable')::text
13927
14423
  AS reported_usage_call_count,
14424
+ COUNT(*) FILTER (WHERE input_tokens IS NOT NULL AND output_tokens IS NOT NULL)::text
14425
+ AS complete_usage_call_count,
13928
14426
  COUNT(input_tokens)::text AS input_tokens_count,
13929
14427
  SUM(input_tokens)::text AS input_tokens_sum,
13930
14428
  COUNT(output_tokens)::text AS output_tokens_count,
@@ -14066,6 +14564,7 @@ function aggregateFragmentFromRow(row) {
14066
14564
  modelCallCount: row.model_call_count,
14067
14565
  usage: {
14068
14566
  reportedCallCount: row.reported_usage_call_count,
14567
+ completeCallCount: row.complete_usage_call_count,
14069
14568
  inputTokens: meterFragment(row.input_tokens_count, row.input_tokens_sum),
14070
14569
  outputTokens: meterFragment(row.output_tokens_count, row.output_tokens_sum),
14071
14570
  uncachedInputTokens: meterFragment(row.uncached_input_tokens_count, row.uncached_input_tokens_sum),
@@ -15049,7 +15548,7 @@ function toStorageRow(row) {
15049
15548
  }
15050
15549
 
15051
15550
  // src/pg-pipeline-run-storage.ts
15052
- import { parseSixbFailure as parseSixbFailure3, serializeSixbFailure as serializeSixbFailure3 } from "@sixb/core/internal/errors";
15551
+ import { parseSixbFailure as parseSixbFailure4, serializeSixbFailure as serializeSixbFailure3 } from "@sixb/core/internal/errors";
15053
15552
  import { assertPipelineRunExecution } from "@sixb/core/internal/pipeline-run-storage-provider";
15054
15553
  import { canRequeuePipelineRunAfterEnqueueFailure } from "@sixb/core/internal/storage";
15055
15554
  import { PIPELINE_RUN_FAILURE_CODES, PipelineRunError } from "@sixb/core/storage";
@@ -15403,7 +15902,7 @@ function rowToPipelineRunRecord(row) {
15403
15902
  datasetId: row.output_dataset_id,
15404
15903
  versionId: row.output_version_id
15405
15904
  } : undefined,
15406
- error: row.error === null ? undefined : parseSixbFailure3(row.error, PIPELINE_RUN_FAILURE_CODES)
15905
+ error: row.error === null ? undefined : parseSixbFailure4(row.error, PIPELINE_RUN_FAILURE_CODES)
15407
15906
  };
15408
15907
  }
15409
15908
  function rowToPipelineStepRunRecord(row) {
@@ -15424,7 +15923,7 @@ function rowToPipelineStepRunRecord(row) {
15424
15923
  versionId: row.output_version_id
15425
15924
  } : undefined,
15426
15925
  rowsWritten: row.rows_written != null ? Number(row.rows_written) : undefined,
15427
- error: row.error === null ? undefined : parseSixbFailure3(row.error, PIPELINE_RUN_FAILURE_CODES)
15926
+ error: row.error === null ? undefined : parseSixbFailure4(row.error, PIPELINE_RUN_FAILURE_CODES)
15428
15927
  };
15429
15928
  }
15430
15929
  function parseDatasetVersionRefs(value) {
@@ -16150,7 +16649,7 @@ function toIsoString3(value) {
16150
16649
  }
16151
16650
 
16152
16651
  // src/pg-sync-run-storage.ts
16153
- import { parseSixbFailure as parseSixbFailure4, serializeSixbFailure as serializeSixbFailure5 } from "@sixb/core/internal/errors";
16652
+ import { parseSixbFailure as parseSixbFailure5, serializeSixbFailure as serializeSixbFailure5 } from "@sixb/core/internal/errors";
16154
16653
  import { canRequeueSyncRunAfterEnqueueFailure } from "@sixb/core/internal/storage";
16155
16654
  import { assertSyncRunExecution } from "@sixb/core/internal/sync-run-storage-provider";
16156
16655
  import { SYNC_RUN_FAILURE_CODES, SyncRunError } from "@sixb/core/storage";
@@ -16376,7 +16875,7 @@ function rowToSyncRunRecord(row) {
16376
16875
  } : undefined,
16377
16876
  expectedLatestVersionId: row.expected_latest_version_id ?? undefined,
16378
16877
  commitMessage: row.commit_message ?? undefined,
16379
- error: row.error === null ? undefined : parseSixbFailure4(row.error, SYNC_RUN_FAILURE_CODES),
16878
+ error: row.error === null ? undefined : parseSixbFailure5(row.error, SYNC_RUN_FAILURE_CODES),
16380
16879
  checkpoint: hasCheckpoint(row) ? row.checkpoint : undefined
16381
16880
  };
16382
16881
  }
@@ -16501,7 +17000,7 @@ function rowToPoint(row) {
16501
17000
  }
16502
17001
 
16503
17002
  // src/pg-webhook-run-storage.ts
16504
- import { parseSixbFailure as parseSixbFailure5, serializeSixbFailure as serializeSixbFailure6 } from "@sixb/core/internal/errors";
17003
+ import { parseSixbFailure as parseSixbFailure6, serializeSixbFailure as serializeSixbFailure6 } from "@sixb/core/internal/errors";
16505
17004
  import { assertWebhookRunExecution } from "@sixb/core/internal/webhook-run-storage-provider";
16506
17005
  import { canRetryWebhookRun, WEBHOOK_RUN_FAILURE_CODES, WebhookRunError } from "@sixb/core/storage";
16507
17006
  class PgWebhookRunStorage {
@@ -16655,7 +17154,7 @@ function rowToWebhookRunRecord(row) {
16655
17154
  requestBodySha256: row.request_body_sha256,
16656
17155
  responseStatus: row.response_status != null ? Number(row.response_status) : undefined,
16657
17156
  idempotencyKey: row.idempotency_key ?? undefined,
16658
- error: row.error === null ? undefined : parseSixbFailure5(row.error, WEBHOOK_RUN_FAILURE_CODES)
17157
+ error: row.error === null ? undefined : parseSixbFailure6(row.error, WEBHOOK_RUN_FAILURE_CODES)
16659
17158
  };
16660
17159
  }
16661
17160
 
@@ -16899,7 +17398,7 @@ function assertNonNegativeInteger(value, fieldName) {
16899
17398
  }
16900
17399
 
16901
17400
  // src/pg-workflow-run-storage.ts
16902
- import { parseSixbFailure as parseSixbFailure6, serializeSixbFailure as serializeSixbFailure7 } from "@sixb/core/internal/errors";
17401
+ import { parseSixbFailure as parseSixbFailure7, serializeSixbFailure as serializeSixbFailure7 } from "@sixb/core/internal/errors";
16903
17402
  import {
16904
17403
  assertWorkflowAgentNodeRunExecution,
16905
17404
  assertWorkflowRunExecution
@@ -17541,7 +18040,7 @@ function rowToWorkflowRunRecord(row) {
17541
18040
  queuedAt: row.queued_at ? new Date(row.queued_at) : undefined,
17542
18041
  startedAt: new Date(row.started_at),
17543
18042
  finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
17544
- error: row.error === null ? undefined : parseSixbFailure6(row.error, WORKFLOW_RUN_FAILURE_CODES),
18043
+ error: row.error === null ? undefined : parseSixbFailure7(row.error, WORKFLOW_RUN_FAILURE_CODES),
17545
18044
  attempt: Number(row.attempt),
17546
18045
  ...row.execution_token && row.execution_queue_lease_expires_at ? {
17547
18046
  execution: {
@@ -17566,7 +18065,7 @@ function rowToWorkflowNodeRunRecord(row) {
17566
18065
  startedAt: new Date(row.started_at),
17567
18066
  finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
17568
18067
  output: row.output ? parseRecord2(row.output) : undefined,
17569
- error: row.error === null ? undefined : parseSixbFailure6(row.error, WORKFLOW_RUN_FAILURE_CODES)
18068
+ error: row.error === null ? undefined : parseSixbFailure7(row.error, WORKFLOW_RUN_FAILURE_CODES)
17570
18069
  };
17571
18070
  }
17572
18071
  function assertNonNegativeInteger2(value, fieldName) {
@@ -17608,7 +18107,7 @@ function rowToWorkflowAgentNodeRunRecord(row) {
17608
18107
  ...row.finish_reason ? { finishReason: row.finish_reason } : {},
17609
18108
  ...row.trace ? { trace: parseJson(row.trace) } : {},
17610
18109
  ...row.diagnostics ? { diagnostics: parseJson(row.diagnostics) } : {},
17611
- ...row.error ? { error: parseSixbFailure6(row.error, AGENT_RUN_FAILURE_CODES3) } : {},
18110
+ ...row.error ? { error: parseSixbFailure7(row.error, AGENT_RUN_FAILURE_CODES3) } : {},
17612
18111
  attempt: Number(row.attempt),
17613
18112
  ...row.execution_token && row.execution_queue_lease_expires_at ? {
17614
18113
  execution: {
@@ -18120,4 +18619,4 @@ export {
18120
18619
  migratePostgresStorage
18121
18620
  };
18122
18621
 
18123
- //# debugId=0CAAAAC02FB5CFD864756E2164756E21
18622
+ //# debugId=C35F49117A3070E564756E2164756E21