@sixb/pg 0.1.10 → 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 (47) 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 +563 -54
  7. package/dist/index.js.map +17 -15
  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/connector-connection-storage.ts +10 -0
  32. package/src/migrations/041-agent-thread-workspaces.sql +2 -0
  33. package/src/migrations/042-vector-profiles.sql +28 -0
  34. package/src/migrations/043-vector-indexing.sql +24 -0
  35. package/src/migrations/044-vector-batching.sql +3 -0
  36. package/src/migrations/045-agent-thread-sandbox-state.sql +2 -0
  37. package/src/migrations.ts +14 -0
  38. package/src/objects/query-compiler.ts +102 -11
  39. package/src/objects/read-scope.ts +26 -25
  40. package/src/objects/reader.ts +33 -1
  41. package/src/objects/rows.ts +5 -1
  42. package/src/objects/storage.ts +20 -2
  43. package/src/ontology-storage/index.ts +15 -0
  44. package/src/ontology-storage/materializations.ts +13 -0
  45. package/src/ontology-storage/vector-indexing.ts +168 -0
  46. package/src/ontology-storage/vectors.ts +172 -0
  47. 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}
@@ -3530,6 +3560,16 @@ class PgConnectorConnectionPersistence {
3530
3560
  throw error;
3531
3561
  }
3532
3562
  }
3563
+ async listPendingConnectionRuns() {
3564
+ if (!this.scope.connectorId)
3565
+ return [];
3566
+ const rows = await this.sql`
3567
+ SELECT * FROM connector_connection_runs
3568
+ WHERE project_id = ${this.scope.projectId} AND connector_id = ${this.scope.connectorId}
3569
+ AND (status = 'running' OR (status = 'waiting' AND waiting_for = 'account_selection'))
3570
+ `;
3571
+ return rows.map(connectionRunFromRow);
3572
+ }
3533
3573
  async getConnectionRun(id) {
3534
3574
  const rows = await this.runById(id);
3535
3575
  return rows[0] ? connectionRunFromRow(rows[0]) : null;
@@ -7130,6 +7170,80 @@ ALTER TABLE projection_runs ADD COLUMN source_changes_read BIGINT
7130
7170
  CHECK (source_changes_read IS NULL OR source_changes_read >= 0);
7131
7171
  `;
7132
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
+
7133
7247
  // src/migrations/037-execution-requester-groups.sql
7134
7248
  var _037_execution_requester_groups_default = `ALTER TABLE executions ADD COLUMN requester_group_ids JSONB NOT NULL DEFAULT '[]'::jsonb;
7135
7249
 
@@ -7382,17 +7496,34 @@ var postgresStorageMigrations = defineMigrations({
7382
7496
  pgSql("037-execution-requester-groups", _037_execution_requester_groups_default),
7383
7497
  pgSql("038-outbox-publication-order", _038_outbox_publication_order_default),
7384
7498
  pgSql("039-projection-source-roots", _039_projection_source_roots_default),
7385
- 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)
7386
7505
  ]
7387
7506
  });
7388
7507
 
7508
+ // src/objects/storage.ts
7509
+ import { hasVectorProfile as hasVectorProfile3 } from "@sixb/core/internal/query";
7510
+
7389
7511
  // src/objects/query-compiler.ts
7390
7512
  import {
7391
7513
  ObjectQueryExecutionError
7392
7514
  } from "@sixb/core";
7515
+ import {
7516
+ hasVectorProfile,
7517
+ isVectorProfileQuery,
7518
+ normalizeVector,
7519
+ vectorSearchCandidateLimit
7520
+ } from "@sixb/core/internal/query";
7393
7521
  var PAGE_TOKEN_PREFIX = "keyset:";
7394
7522
  var DEFAULT_EXPANSION_FANOUT = 1000;
7395
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
+ }
7396
7527
  const source = options.source ?? DEFAULT_OBJECT_QUERY_SOURCE;
7397
7528
  const compiled = source.wrapQuery(compileObjectQueryInternal(projectId, query, {
7398
7529
  probeLimit: options.includeTotal === false,
@@ -7402,6 +7533,7 @@ function compilePgObjectQuery(projectId, query, options = {}) {
7402
7533
  ...compiled,
7403
7534
  sql: numberPlaceholders(compiled.sql),
7404
7535
  totalSql: numberPlaceholders(compiled.totalSql),
7536
+ vectorProbe: compiled.vectorProbe ? { ...compiled.vectorProbe, sql: numberPlaceholders(compiled.vectorProbe.sql) } : undefined,
7405
7537
  hasMoreProbe: compiled.hasMoreProbe ? {
7406
7538
  ...compiled.hasMoreProbe,
7407
7539
  sql: numberPlaceholders(compiled.hasMoreProbe.sql)
@@ -7479,8 +7611,56 @@ function compileObjectQueryInternal(projectId, query, ctx) {
7479
7611
  case "expand":
7480
7612
  return compileExpand(projectId, query.input, query.expansions, ctx);
7481
7613
  case "vector":
7482
- throw new Error(`[SixbPg] PostgreSQL object storage does not support query node '${query.kind}'`);
7483
- }
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
+ };
7484
7664
  }
7485
7665
  function compileStart(projectId, query, ctx) {
7486
7666
  if (query.includeSubtypes === true) {
@@ -7568,6 +7748,7 @@ function compileWhere(projectId, inputQuery, predicate, ctx) {
7568
7748
  WHERE ${predicate.sql}
7569
7749
  `,
7570
7750
  totalArgs: [...input.args, ...predicate.args],
7751
+ vectorProbe: input.vectorProbe,
7571
7752
  order: input.order,
7572
7753
  hasMore: () => false,
7573
7754
  trimRows: identityRows,
@@ -7594,7 +7775,7 @@ function compileSort(projectId, inputQuery, fields, ctx) {
7594
7775
  input.created_at,
7595
7776
  input.updated_at,
7596
7777
  input.version,
7597
- input.last_commit_id
7778
+ input.last_commit_id${input.vectorProbe ? ", input._vector_score" : ""}
7598
7779
  FROM (${input.sql}) AS input
7599
7780
  ORDER BY ${order.sql}
7600
7781
  `,
@@ -7625,6 +7806,7 @@ function compileLimit(projectId, inputQuery, rawLimit, ctx) {
7625
7806
  FROM (${input.sql}) AS input
7626
7807
  `,
7627
7808
  totalArgs: input.args,
7809
+ vectorProbe: input.vectorProbe,
7628
7810
  order: input.order,
7629
7811
  hasMore: (rowCount, total) => total === undefined ? ctx.probeLimit && rowCount > limit : limit < total,
7630
7812
  trimRows: ctx.probeLimit ? (rows) => rows.slice(0, limit) : identityRows,
@@ -7653,6 +7835,7 @@ function compilePage(projectId, inputQuery, rawPageSize, pageToken, ctx) {
7653
7835
  FROM (${input.sql}) AS input
7654
7836
  `,
7655
7837
  totalArgs: input.args,
7838
+ vectorProbe: input.vectorProbe,
7656
7839
  order: input.order,
7657
7840
  hasMore: (rowCount) => rowCount > pageSize,
7658
7841
  trimRows: (rows) => rows.slice(0, pageSize),
@@ -7729,6 +7912,7 @@ function compileExpand(projectId, inputQuery, expansions, ctx) {
7729
7912
  args: [...expand.args, ...input.args, ...inputOrder.args],
7730
7913
  totalSql: input.totalSql,
7731
7914
  totalArgs: input.totalArgs,
7915
+ vectorProbe: input.vectorProbe,
7732
7916
  order: input.order,
7733
7917
  hasMoreProbe: input.hasMoreProbe,
7734
7918
  hasMore: input.hasMore,
@@ -7894,13 +8078,14 @@ function compileProject(projectId, inputQuery, properties, ctx) {
7894
8078
  input.created_at,
7895
8079
  input.updated_at,
7896
8080
  input.version,
7897
- input.last_commit_id
8081
+ input.last_commit_id${input.vectorProbe ? ", input._vector_score" : ""}
7898
8082
  FROM (${input.sql}) AS input
7899
8083
  ORDER BY ${inputOrder.sql}
7900
8084
  `,
7901
8085
  args: [...projection.args, ...input.args, ...inputOrder.args],
7902
8086
  totalSql: input.totalSql,
7903
8087
  totalArgs: input.totalArgs,
8088
+ vectorProbe: input.vectorProbe,
7904
8089
  order: outputOrder,
7905
8090
  hasMoreProbe: input.hasMoreProbe,
7906
8091
  hasMore: input.hasMore,
@@ -7931,7 +8116,7 @@ function compileAggregateSource(projectId, query, source) {
7931
8116
  case "page":
7932
8117
  return compileRowQueryAggregateSource(projectId, query, source);
7933
8118
  case "vector":
7934
- throw new Error(`[SixbPg] PostgreSQL object storage does not support query node '${query.kind}'`);
8119
+ return compileRowQueryAggregateSource(projectId, query, source);
7935
8120
  }
7936
8121
  }
7937
8122
  function compileAggregateStart(projectId, query, source) {
@@ -8300,7 +8485,7 @@ function compileOrder(fields, qualifier, propertyColumn = "properties") {
8300
8485
  const args = [];
8301
8486
  for (const field of fields) {
8302
8487
  if (field.kind === "column") {
8303
- clauses.push(`${columnExpression(field.column, qualifier)} ${field.direction.toUpperCase()}`);
8488
+ clauses.push(`${columnExpression(field.column, qualifier)}${field.binary ? ' COLLATE "C"' : ""} ${field.direction.toUpperCase()}`);
8304
8489
  continue;
8305
8490
  }
8306
8491
  const direction = field.direction === "desc" ? "DESC" : "ASC";
@@ -8509,6 +8694,8 @@ function isPlainObject(value) {
8509
8694
  }
8510
8695
 
8511
8696
  // src/objects/reader.ts
8697
+ import { ObjectQueryExecutionError as ObjectQueryExecutionError2 } from "@sixb/core";
8698
+ import { hasVectorProfile as hasVectorProfile2 } from "@sixb/core/internal/query";
8512
8699
  import { linkBatchKey, objectBatchKey } from "@sixb/core/storage";
8513
8700
 
8514
8701
  // src/objects/rows.ts
@@ -8525,7 +8712,10 @@ function rowToObject(row) {
8525
8712
  };
8526
8713
  }
8527
8714
  function queryRowToObject(row) {
8528
- const base = rowToObject(row);
8715
+ const base = {
8716
+ ...rowToObject(row),
8717
+ ...row._vector_score === undefined ? {} : { score: row._vector_score }
8718
+ };
8529
8719
  const links = reviveExpandedLinks(row._expand);
8530
8720
  return links ? { ...base, links } : base;
8531
8721
  }
@@ -8600,7 +8790,23 @@ class PgObjectReader {
8600
8790
  this.sql = sql;
8601
8791
  this.source = source;
8602
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
+ }
8603
8808
  async queryObjects(params) {
8809
+ await this.assertVectorSearch(params);
8604
8810
  const sql = this.sql;
8605
8811
  const source = this.source;
8606
8812
  const compiled = compilePgObjectQuery(params.projectId, params.query, {
@@ -8619,6 +8825,7 @@ class PgObjectReader {
8619
8825
  };
8620
8826
  }
8621
8827
  async countObjects(params) {
8828
+ await this.assertVectorSearch(params);
8622
8829
  const sql = this.sql;
8623
8830
  const source = this.source;
8624
8831
  const compiled = compilePgObjectCountQuery(params.projectId, stripOuterRowShape(params.query), {
@@ -8628,6 +8835,7 @@ class PgObjectReader {
8628
8835
  return { count: Number(row?.count ?? 0) };
8629
8836
  }
8630
8837
  async existsObjects(params) {
8838
+ await this.assertVectorSearch(params);
8631
8839
  const sql = this.sql;
8632
8840
  const source = this.source;
8633
8841
  const compiled = compilePgObjectExistsQuery(params.projectId, stripOuterRowShape(params.query), { source });
@@ -8635,6 +8843,7 @@ class PgObjectReader {
8635
8843
  return { exists: row !== undefined };
8636
8844
  }
8637
8845
  async facetObjects(params) {
8846
+ await this.assertVectorSearch(params);
8638
8847
  const sql = this.sql;
8639
8848
  const source = this.source;
8640
8849
  const facets = [];
@@ -9022,6 +9231,7 @@ ${sql}`,
9022
9231
  wrapQuery: (query) => ({
9023
9232
  ...query,
9024
9233
  ...wrapStatement(query.sql, query.args),
9234
+ vectorProbe: query.vectorProbe ? { ...query.vectorProbe, ...wrapStatement(query.vectorProbe.sql, query.vectorProbe.args) } : undefined,
9025
9235
  totalSql: `${compiled.sql}
9026
9236
  ${query.totalSql}`,
9027
9237
  totalArgs: [...compiled.args, ...query.totalArgs],
@@ -9143,32 +9353,29 @@ function compileSelectedReadScopeCte(scopeJson, projectId) {
9143
9353
  SELECT DISTINCT project_id, object_type_id, primary_id
9144
9354
  FROM _sixb_scope_reachable
9145
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
+ ),
9146
9366
  _sixb_scope_objects AS (
9147
- SELECT
9148
- stored.project_id,
9149
- stored.object_type_id,
9150
- stored.primary_id,
9151
- COALESCE(
9152
- (
9153
- SELECT jsonb_object_agg(property.key, property.value)
9154
- FROM jsonb_each(stored.properties) AS property(key, value)
9155
- JOIN _sixb_scope_object_permissions AS permission
9156
- ON permission.project_id = stored.project_id
9157
- AND permission.object_type_id = stored.object_type_id
9158
- AND permission.primary_id = stored.primary_id
9159
- AND permission.property_id = property.key
9160
- ),
9161
- '{}'::jsonb
9162
- ) AS properties,
9163
- stored.created_at,
9164
- stored.updated_at,
9165
- stored.version,
9166
- stored.last_commit_id
9167
- FROM objects AS stored
9168
- JOIN _sixb_scope_object_ids AS visible
9169
- ON visible.project_id = stored.project_id
9170
- AND visible.object_type_id = stored.object_type_id
9171
- 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
9172
9379
  ),
9173
9380
  _sixb_scope_reached_links(
9174
9381
  project_id,
@@ -9375,6 +9582,7 @@ var PG_OBJECT_QUERY_CAPABILITIES = {
9375
9582
  refs: true,
9376
9583
  filter: true,
9377
9584
  text: true,
9585
+ vector: true,
9378
9586
  sort: true,
9379
9587
  limit: true,
9380
9588
  page: true,
@@ -9427,7 +9635,7 @@ var PG_OBJECT_QUERY_CAPABILITIES = {
9427
9635
  "PostgreSQL object query pushdown supports start/refs/filter/text/sort/limit/page/traverse/set/project/expand over JSONB properties and object links.",
9428
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.",
9429
9637
  "Exact decimal predicates, ordering, and keyset pagination use PostgreSQL numeric casts.",
9430
- "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."
9431
9639
  ]
9432
9640
  };
9433
9641
 
@@ -9445,15 +9653,23 @@ class PgObjectStorage {
9445
9653
  return createPgSelectedReader(this.sql, params, () => this.queryCapabilities());
9446
9654
  }
9447
9655
  async queryObjects(params) {
9656
+ if (hasVectorProfile3(params.query))
9657
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).queryObjects(params));
9448
9658
  return this.reader.queryObjects(params);
9449
9659
  }
9450
9660
  async countObjects(params) {
9661
+ if (hasVectorProfile3(params.query))
9662
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).countObjects(params));
9451
9663
  return this.reader.countObjects(params);
9452
9664
  }
9453
9665
  async existsObjects(params) {
9666
+ if (hasVectorProfile3(params.query))
9667
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).existsObjects(params));
9454
9668
  return this.reader.existsObjects(params);
9455
9669
  }
9456
9670
  async facetObjects(params) {
9671
+ if (hasVectorProfile3(params.query))
9672
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).facetObjects(params));
9457
9673
  return this.reader.facetObjects(params);
9458
9674
  }
9459
9675
  async getByPrimaryId(params) {
@@ -9756,6 +9972,7 @@ import {
9756
9972
  linkRefSortKey,
9757
9973
  linkScopeSortKey as linkScopeSortKey2,
9758
9974
  MaterializationConflictError as MaterializationConflictError6,
9975
+ MaterializationValidationError as MaterializationValidationError2,
9759
9976
  objectRefKey as objectRefKey3,
9760
9977
  objectRefSortKey,
9761
9978
  telemetryPointKey as telemetryPointKey3,
@@ -11682,6 +11899,12 @@ class PgOntologyMaterializationStorage {
11682
11899
  this.sessions = new PgMaterializationSessions(sql, context);
11683
11900
  this.writer = new PgMaterializationWriter(sql);
11684
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
+ }
11685
11908
  async begin(input) {
11686
11909
  assertMaterializationHeader(input);
11687
11910
  const session = await this.sessions.create(input);
@@ -12289,7 +12512,7 @@ function databaseCount(value) {
12289
12512
  import { serializeSixbFailure as serializeSixbFailure2 } from "@sixb/core/internal/errors";
12290
12513
  import {
12291
12514
  MaterializationConflictError as MaterializationConflictError7,
12292
- MaterializationValidationError as MaterializationValidationError2
12515
+ MaterializationValidationError as MaterializationValidationError3
12293
12516
  } from "@sixb/core/internal/materialization";
12294
12517
  import { ONTOLOGY_OUTBOX_FAILURE_CODES as ONTOLOGY_OUTBOX_FAILURE_CODES2 } from "@sixb/core/storage";
12295
12518
  class PgOntologyOutboxStorage {
@@ -12305,7 +12528,7 @@ class PgOntologyOutboxStorage {
12305
12528
  const leaseExpiry = assertTimestamp(input.leaseExpiresAt, "Ontology outbox lease expiry");
12306
12529
  assertNonblank(input.leaseId, "Ontology outbox lease id");
12307
12530
  if (leaseExpiry <= now) {
12308
- 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.");
12309
12532
  }
12310
12533
  const rows = await sql`
12311
12534
  WITH candidates AS (
@@ -12456,7 +12679,7 @@ function validateLeaseIds(ids) {
12456
12679
  for (const id of ids) {
12457
12680
  assertNonblank(id, "Ontology outbox event id");
12458
12681
  if (seen.has(id)) {
12459
- throw new MaterializationValidationError2(`Ontology outbox lease batch repeats event '${id}'.`);
12682
+ throw new MaterializationValidationError3(`Ontology outbox lease batch repeats event '${id}'.`);
12460
12683
  }
12461
12684
  seen.add(id);
12462
12685
  }
@@ -12471,7 +12694,7 @@ function leaseConflict() {
12471
12694
  }
12472
12695
 
12473
12696
  // src/ontology-storage/sources.ts
12474
- import { MaterializationValidationError as MaterializationValidationError3 } from "@sixb/core/internal/materialization";
12697
+ import { MaterializationValidationError as MaterializationValidationError4 } from "@sixb/core/internal/materialization";
12475
12698
  import {
12476
12699
  assertSourceBeginInput as assertBeginInput,
12477
12700
  assertSourceExecutionIdentity as assertExecutionIdentity,
@@ -12631,7 +12854,7 @@ class PgOntologySourceStorage {
12631
12854
  throw sourceConflict(`Source materialization '${input.materializationId}' cannot transition from '${manifest.status}' to 'ready'.`);
12632
12855
  }
12633
12856
  if (input.readyAt < toIsoString2(manifest.created_at)) {
12634
- throw new MaterializationValidationError3("Source readyAt cannot precede source createdAt.");
12857
+ throw new MaterializationValidationError4("Source readyAt cannot precede source createdAt.");
12635
12858
  }
12636
12859
  if (input.assertionCount >= 1000 || input.rootCount >= 1000) {
12637
12860
  await sql`ANALYZE ontology_source_rows, ontology_source_roots`;
@@ -12729,7 +12952,7 @@ class PgOntologySourceStorage {
12729
12952
  }
12730
12953
  async transitionToAbandoned(sql, manifest, abandonedAt) {
12731
12954
  if (abandonedAt < toIsoString2(manifest.created_at) || manifest.ready_at !== null && abandonedAt < toIsoString2(manifest.ready_at)) {
12732
- throw new MaterializationValidationError3("Source abandonedAt cannot precede source creation or readiness.");
12955
+ throw new MaterializationValidationError4("Source abandonedAt cannot precede source creation or readiness.");
12733
12956
  }
12734
12957
  const rows = await sql`
12735
12958
  UPDATE ontology_sources
@@ -12767,7 +12990,7 @@ class PgOntologySourceStorage {
12767
12990
  const minimum = counts?.min_ordinal === null ? null : Number(counts?.min_ordinal);
12768
12991
  const maximum = counts?.max_ordinal === null ? null : Number(counts?.max_ordinal);
12769
12992
  if (assertions !== assertionCount || roots !== rootCount || ordinals !== rootCount || rootCount > 0 && (minimum !== 0 || maximum !== rootCount - 1)) {
12770
- 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.");
12771
12994
  }
12772
12995
  await assertSourceRootCoverage(sql, manifest);
12773
12996
  const [invalid] = manifest.projection_kind === "link" ? await sql`
@@ -12803,7 +13026,7 @@ class PgOntologySourceStorage {
12803
13026
  LIMIT 1
12804
13027
  `;
12805
13028
  if (invalid) {
12806
- 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.");
12807
13030
  }
12808
13031
  }
12809
13032
  async findStageRows(sql, input, rows) {
@@ -12935,17 +13158,300 @@ function numberOrNull(value) {
12935
13158
  return value === null ? null : Number(value);
12936
13159
  }
12937
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
+
12938
13440
  // src/ontology-storage/index.ts
12939
13441
  class PgOntologyStorage {
12940
13442
  commits;
12941
13443
  sources;
12942
13444
  materializations;
12943
13445
  outbox;
13446
+ vectorIndexing;
13447
+ vectors;
12944
13448
  constructor(input) {
12945
13449
  this.commits = new PgOntologyCommitStorage(input.runRootOperation);
12946
13450
  this.sources = new PgOntologySourceStorage(input.runRootOperation);
12947
13451
  this.materializations = new PgOntologyMaterializationStorage(input.sql, input.transactionContext);
12948
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));
12949
13455
  }
12950
13456
  deactivateSessions() {
12951
13457
  this.materializations.deactivateSessions();
@@ -13915,6 +14421,8 @@ class PgAiCostStorage {
13915
14421
  COUNT(*)::text AS model_call_count,
13916
14422
  COUNT(*) FILTER (WHERE reporting_status <> 'unavailable')::text
13917
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,
13918
14426
  COUNT(input_tokens)::text AS input_tokens_count,
13919
14427
  SUM(input_tokens)::text AS input_tokens_sum,
13920
14428
  COUNT(output_tokens)::text AS output_tokens_count,
@@ -14056,6 +14564,7 @@ function aggregateFragmentFromRow(row) {
14056
14564
  modelCallCount: row.model_call_count,
14057
14565
  usage: {
14058
14566
  reportedCallCount: row.reported_usage_call_count,
14567
+ completeCallCount: row.complete_usage_call_count,
14059
14568
  inputTokens: meterFragment(row.input_tokens_count, row.input_tokens_sum),
14060
14569
  outputTokens: meterFragment(row.output_tokens_count, row.output_tokens_sum),
14061
14570
  uncachedInputTokens: meterFragment(row.uncached_input_tokens_count, row.uncached_input_tokens_sum),
@@ -15039,7 +15548,7 @@ function toStorageRow(row) {
15039
15548
  }
15040
15549
 
15041
15550
  // src/pg-pipeline-run-storage.ts
15042
- 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";
15043
15552
  import { assertPipelineRunExecution } from "@sixb/core/internal/pipeline-run-storage-provider";
15044
15553
  import { canRequeuePipelineRunAfterEnqueueFailure } from "@sixb/core/internal/storage";
15045
15554
  import { PIPELINE_RUN_FAILURE_CODES, PipelineRunError } from "@sixb/core/storage";
@@ -15393,7 +15902,7 @@ function rowToPipelineRunRecord(row) {
15393
15902
  datasetId: row.output_dataset_id,
15394
15903
  versionId: row.output_version_id
15395
15904
  } : undefined,
15396
- 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)
15397
15906
  };
15398
15907
  }
15399
15908
  function rowToPipelineStepRunRecord(row) {
@@ -15414,7 +15923,7 @@ function rowToPipelineStepRunRecord(row) {
15414
15923
  versionId: row.output_version_id
15415
15924
  } : undefined,
15416
15925
  rowsWritten: row.rows_written != null ? Number(row.rows_written) : undefined,
15417
- 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)
15418
15927
  };
15419
15928
  }
15420
15929
  function parseDatasetVersionRefs(value) {
@@ -16140,7 +16649,7 @@ function toIsoString3(value) {
16140
16649
  }
16141
16650
 
16142
16651
  // src/pg-sync-run-storage.ts
16143
- 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";
16144
16653
  import { canRequeueSyncRunAfterEnqueueFailure } from "@sixb/core/internal/storage";
16145
16654
  import { assertSyncRunExecution } from "@sixb/core/internal/sync-run-storage-provider";
16146
16655
  import { SYNC_RUN_FAILURE_CODES, SyncRunError } from "@sixb/core/storage";
@@ -16366,7 +16875,7 @@ function rowToSyncRunRecord(row) {
16366
16875
  } : undefined,
16367
16876
  expectedLatestVersionId: row.expected_latest_version_id ?? undefined,
16368
16877
  commitMessage: row.commit_message ?? undefined,
16369
- 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),
16370
16879
  checkpoint: hasCheckpoint(row) ? row.checkpoint : undefined
16371
16880
  };
16372
16881
  }
@@ -16491,7 +17000,7 @@ function rowToPoint(row) {
16491
17000
  }
16492
17001
 
16493
17002
  // src/pg-webhook-run-storage.ts
16494
- 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";
16495
17004
  import { assertWebhookRunExecution } from "@sixb/core/internal/webhook-run-storage-provider";
16496
17005
  import { canRetryWebhookRun, WEBHOOK_RUN_FAILURE_CODES, WebhookRunError } from "@sixb/core/storage";
16497
17006
  class PgWebhookRunStorage {
@@ -16645,7 +17154,7 @@ function rowToWebhookRunRecord(row) {
16645
17154
  requestBodySha256: row.request_body_sha256,
16646
17155
  responseStatus: row.response_status != null ? Number(row.response_status) : undefined,
16647
17156
  idempotencyKey: row.idempotency_key ?? undefined,
16648
- 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)
16649
17158
  };
16650
17159
  }
16651
17160
 
@@ -16889,7 +17398,7 @@ function assertNonNegativeInteger(value, fieldName) {
16889
17398
  }
16890
17399
 
16891
17400
  // src/pg-workflow-run-storage.ts
16892
- 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";
16893
17402
  import {
16894
17403
  assertWorkflowAgentNodeRunExecution,
16895
17404
  assertWorkflowRunExecution
@@ -17531,7 +18040,7 @@ function rowToWorkflowRunRecord(row) {
17531
18040
  queuedAt: row.queued_at ? new Date(row.queued_at) : undefined,
17532
18041
  startedAt: new Date(row.started_at),
17533
18042
  finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
17534
- 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),
17535
18044
  attempt: Number(row.attempt),
17536
18045
  ...row.execution_token && row.execution_queue_lease_expires_at ? {
17537
18046
  execution: {
@@ -17556,7 +18065,7 @@ function rowToWorkflowNodeRunRecord(row) {
17556
18065
  startedAt: new Date(row.started_at),
17557
18066
  finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
17558
18067
  output: row.output ? parseRecord2(row.output) : undefined,
17559
- 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)
17560
18069
  };
17561
18070
  }
17562
18071
  function assertNonNegativeInteger2(value, fieldName) {
@@ -17598,7 +18107,7 @@ function rowToWorkflowAgentNodeRunRecord(row) {
17598
18107
  ...row.finish_reason ? { finishReason: row.finish_reason } : {},
17599
18108
  ...row.trace ? { trace: parseJson(row.trace) } : {},
17600
18109
  ...row.diagnostics ? { diagnostics: parseJson(row.diagnostics) } : {},
17601
- ...row.error ? { error: parseSixbFailure6(row.error, AGENT_RUN_FAILURE_CODES3) } : {},
18110
+ ...row.error ? { error: parseSixbFailure7(row.error, AGENT_RUN_FAILURE_CODES3) } : {},
17602
18111
  attempt: Number(row.attempt),
17603
18112
  ...row.execution_token && row.execution_queue_lease_expires_at ? {
17604
18113
  execution: {
@@ -18110,4 +18619,4 @@ export {
18110
18619
  migratePostgresStorage
18111
18620
  };
18112
18621
 
18113
- //# debugId=672C519862E8368164756E2164756E21
18622
+ //# debugId=C35F49117A3070E564756E2164756E21