@sixb/pg 0.1.11 → 0.1.13

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 (74) 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/auth-storage/access-tokens.d.ts.map +1 -1
  7. package/dist/auth-storage/device-authorizations.d.ts.map +1 -1
  8. package/dist/auth-storage/rows.d.ts +6 -3
  9. package/dist/auth-storage/rows.d.ts.map +1 -1
  10. package/dist/auth-storage/sessions.d.ts +8 -1
  11. package/dist/auth-storage/sessions.d.ts.map +1 -1
  12. package/dist/auth-storage/shared.d.ts.map +1 -1
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +917 -122
  16. package/dist/index.js.map +26 -23
  17. package/dist/migrations.d.ts.map +1 -1
  18. package/dist/objects/query-compiler.d.ts +8 -1
  19. package/dist/objects/query-compiler.d.ts.map +1 -1
  20. package/dist/objects/read-scope.d.ts +1 -0
  21. package/dist/objects/read-scope.d.ts.map +1 -1
  22. package/dist/objects/reader.d.ts +1 -0
  23. package/dist/objects/reader.d.ts.map +1 -1
  24. package/dist/objects/rows.d.ts +1 -0
  25. package/dist/objects/rows.d.ts.map +1 -1
  26. package/dist/objects/storage.d.ts +1 -1
  27. package/dist/objects/storage.d.ts.map +1 -1
  28. package/dist/ontology-storage/index.d.ts +4 -0
  29. package/dist/ontology-storage/index.d.ts.map +1 -1
  30. package/dist/ontology-storage/materializations.d.ts +1 -2
  31. package/dist/ontology-storage/materializations.d.ts.map +1 -1
  32. package/dist/ontology-storage/shared.d.ts +2 -1
  33. package/dist/ontology-storage/shared.d.ts.map +1 -1
  34. package/dist/ontology-storage/vector-indexing.d.ts +20 -0
  35. package/dist/ontology-storage/vector-indexing.d.ts.map +1 -0
  36. package/dist/ontology-storage/vectors.d.ts +16 -0
  37. package/dist/ontology-storage/vectors.d.ts.map +1 -0
  38. package/dist/pg-ai-cost-storage.d.ts.map +1 -1
  39. package/dist/pg-file-upload-session-storage.d.ts +6 -0
  40. package/dist/pg-file-upload-session-storage.d.ts.map +1 -0
  41. package/dist/pg-workflow-intervention-storage.d.ts.map +1 -1
  42. package/package.json +3 -3
  43. package/src/agents/rows.ts +21 -0
  44. package/src/agents/threads.ts +47 -2
  45. package/src/auth-storage/access-tokens.ts +10 -14
  46. package/src/auth-storage/auth-storage.ts +4 -6
  47. package/src/auth-storage/device-authorizations.ts +3 -4
  48. package/src/auth-storage/rows.ts +17 -7
  49. package/src/auth-storage/sessions.ts +82 -4
  50. package/src/auth-storage/shared.ts +8 -2
  51. package/src/index.ts +5 -0
  52. package/src/migrations/041-agent-thread-workspaces.sql +2 -0
  53. package/src/migrations/042-vector-profiles.sql +28 -0
  54. package/src/migrations/043-vector-indexing.sql +24 -0
  55. package/src/migrations/044-vector-batching.sql +3 -0
  56. package/src/migrations/045-agent-thread-sandbox-state.sql +2 -0
  57. package/src/migrations/046-workflow-intervention-principals.sql +15 -0
  58. package/src/migrations/047-ontology-commit-attribution.sql +50 -0
  59. package/src/migrations/048-file-upload-sessions.sql +34 -0
  60. package/src/migrations/049-native-sessions.sql +19 -0
  61. package/src/migrations.ts +26 -0
  62. package/src/objects/query-compiler.ts +102 -11
  63. package/src/objects/read-scope.ts +26 -25
  64. package/src/objects/reader.ts +33 -1
  65. package/src/objects/rows.ts +5 -1
  66. package/src/objects/storage.ts +22 -2
  67. package/src/ontology-storage/index.ts +15 -0
  68. package/src/ontology-storage/materializations.ts +28 -58
  69. package/src/ontology-storage/shared.ts +7 -3
  70. package/src/ontology-storage/vector-indexing.ts +168 -0
  71. package/src/ontology-storage/vectors.ts +172 -0
  72. package/src/pg-ai-cost-storage.ts +4 -0
  73. package/src/pg-file-upload-session-storage.ts +143 -0
  74. package/src/pg-workflow-intervention-storage.ts +11 -15
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}
@@ -1132,7 +1162,14 @@ function rowToSessionRecord(row) {
1132
1162
  revokedAt: row.revoked_at ? toDate(row.revoked_at) : undefined,
1133
1163
  lastSeenAt: row.last_seen_at ? toDate(row.last_seen_at) : undefined,
1134
1164
  userAgent: row.user_agent ?? undefined,
1135
- ipAddress: row.ip_address ?? undefined
1165
+ ipAddress: row.ip_address ?? undefined,
1166
+ bearer: row.refresh_token_hash !== null && row.client_name !== null && row.access_expires_at !== null ? {
1167
+ clientName: row.client_name,
1168
+ accessExpiresAt: toDate(row.access_expires_at),
1169
+ refreshTokenHash: row.refresh_token_hash,
1170
+ previousRefreshTokenHash: row.previous_refresh_token_hash ?? undefined,
1171
+ refreshedAt: row.refreshed_at ? toDate(row.refreshed_at) : undefined
1172
+ } : undefined
1136
1173
  };
1137
1174
  }
1138
1175
  function rowToAccessTokenRecord(row) {
@@ -1141,8 +1178,7 @@ function rowToAccessTokenRecord(row) {
1141
1178
  projectId: row.project_id,
1142
1179
  name: row.name,
1143
1180
  kind: row.kind,
1144
- subjectType: row.subject_type,
1145
- subjectId: row.subject_id,
1181
+ subject: { type: row.subject_type, id: row.subject_id },
1146
1182
  tokenHash: row.token_hash,
1147
1183
  groupIds: parseOptionalStringArray(row.group_ids),
1148
1184
  createdByPrincipal: principalFromColumns2(row),
@@ -1217,8 +1253,6 @@ function rowToDeviceAuthorizationRecord(row) {
1217
1253
  deviceCodeHash: row.device_code_hash,
1218
1254
  userCode: row.user_code,
1219
1255
  clientName: row.client_name,
1220
- tokenName: row.token_name,
1221
- tokenExpiresAt: toDate(row.token_expires_at),
1222
1256
  status: row.status,
1223
1257
  approvedUserId: row.approved_user_id ?? undefined,
1224
1258
  approvedSessionId: row.approved_session_id ?? undefined,
@@ -1529,7 +1563,10 @@ async function createSession(sql, input) {
1529
1563
  expires_at,
1530
1564
  absolute_expires_at,
1531
1565
  user_agent,
1532
- ip_address
1566
+ ip_address,
1567
+ client_name,
1568
+ access_expires_at,
1569
+ refresh_token_hash
1533
1570
  ) VALUES (
1534
1571
  ${projectId},
1535
1572
  ${id},
@@ -1541,7 +1578,10 @@ async function createSession(sql, input) {
1541
1578
  ${input.expiresAt},
1542
1579
  ${input.absoluteExpiresAt ?? null},
1543
1580
  ${input.userAgent ?? null},
1544
- ${input.ipAddress ?? null}
1581
+ ${input.ipAddress ?? null},
1582
+ ${input.bearer ? assertNonEmpty(input.bearer.clientName, "Session client name") : null},
1583
+ ${input.bearer?.accessExpiresAt ?? null},
1584
+ ${input.bearer ? assertNonEmpty(input.bearer.refreshTokenHash, "Refresh token hash") : null}
1545
1585
  )
1546
1586
  RETURNING *
1547
1587
  `;
@@ -1733,10 +1773,10 @@ class PgAuthAccessTokenStore {
1733
1773
  const id = assertNonEmpty(input.id, "Access token id");
1734
1774
  const projectId = assertNonEmpty(input.projectId, "Project id");
1735
1775
  const name = assertNonEmpty(input.name, "Access token name");
1736
- const subjectId = assertNonEmpty(input.subjectId, "Access token subject id");
1776
+ const subjectId = assertNonEmpty(input.subject.id, "Access token subject id");
1737
1777
  const tokenHash = assertNonEmpty(input.tokenHash, "Access token hash");
1738
1778
  assertKindMatchesSubject(input);
1739
- await this.assertSubjectExists(projectId, input.subjectType, subjectId);
1779
+ await this.assertSubjectExists(projectId, input.subject.type, subjectId);
1740
1780
  const groupIds = input.groupIds === undefined ? undefined : normalizeGroupIds(input.groupIds);
1741
1781
  try {
1742
1782
  const [row] = await this.sql`
@@ -1760,7 +1800,7 @@ class PgAuthAccessTokenStore {
1760
1800
  ${id},
1761
1801
  ${name},
1762
1802
  ${input.kind},
1763
- ${input.subjectType},
1803
+ ${input.subject.type},
1764
1804
  ${subjectId},
1765
1805
  ${tokenHash},
1766
1806
  ${serializeOptionalStringArray(groupIds)},
@@ -1795,13 +1835,9 @@ class PgAuthAccessTokenStore {
1795
1835
  whereClauses.push(`kind = $${nextIndex++}`);
1796
1836
  params.push(input.kind);
1797
1837
  }
1798
- if (input.subjectType) {
1799
- whereClauses.push(`subject_type = $${nextIndex++}`);
1800
- params.push(input.subjectType);
1801
- }
1802
- if (input.subjectId) {
1803
- whereClauses.push(`subject_id = $${nextIndex++}`);
1804
- params.push(input.subjectId);
1838
+ if (input.subject) {
1839
+ whereClauses.push(`subject_type = $${nextIndex++}`, `subject_id = $${nextIndex++}`);
1840
+ params.push(input.subject.type, input.subject.id);
1805
1841
  }
1806
1842
  if (!input.includeRevoked) {
1807
1843
  whereClauses.push("revoked_at IS NULL");
@@ -1889,10 +1925,10 @@ class PgAuthAccessTokenStore {
1889
1925
  }
1890
1926
  }
1891
1927
  function assertKindMatchesSubject(input) {
1892
- if (input.kind === "personal" && input.subjectType === "user" || input.kind === "serviceAccount" && input.subjectType === "serviceAccount") {
1928
+ if (input.kind === "personal" && input.subject.type === "user" || input.kind === "serviceAccount" && input.subject.type === "serviceAccount") {
1893
1929
  return;
1894
1930
  }
1895
- throw new AuthStorageError2("invalid_input", `[Sixb] Access token kind '${input.kind}' cannot target subject type '${input.subjectType}'.`);
1931
+ throw new AuthStorageError2("invalid_input", `[Sixb] Access token kind '${input.kind}' cannot target subject type '${input.subject.type}'.`);
1896
1932
  }
1897
1933
 
1898
1934
  // src/auth-storage/device-authorizations.ts
@@ -1919,16 +1955,15 @@ class PgAuthDeviceAuthorizationStore {
1919
1955
  try {
1920
1956
  const [row] = await tx`
1921
1957
  INSERT INTO auth_device_authorizations (
1922
- project_id, id, device_code_hash, user_code, client_name, token_name,
1923
- token_expires_at, status, created_at, expires_at
1958
+ project_id, id, device_code_hash, user_code, client_name, status, created_at,
1959
+ expires_at
1924
1960
  ) VALUES (
1925
1961
  ${assertNonEmpty(input.projectId, "Project id")},
1926
1962
  ${assertNonEmpty(input.id, "Device authorization id")},
1927
1963
  ${assertNonEmpty(input.deviceCodeHash, "Device code hash")},
1928
1964
  ${assertNonEmpty(input.userCode, "User code")},
1929
1965
  ${assertNonEmpty(input.clientName, "Client name")},
1930
- ${assertNonEmpty(input.tokenName, "Token name")},
1931
- ${input.tokenExpiresAt}, 'pending', ${input.createdAt}, ${input.expiresAt}
1966
+ 'pending', ${input.createdAt}, ${input.expiresAt}
1932
1967
  ) RETURNING *
1933
1968
  `;
1934
1969
  return rowToDeviceAuthorizationRecord(row);
@@ -2670,11 +2705,53 @@ class PgAuthSessionStore {
2670
2705
  }
2671
2706
  async findValidByTokenHash(params) {
2672
2707
  const row = await getSessionRowById(this.sql, params);
2673
- if (!row || row.audience !== params.audience || row.token_hash !== params.tokenHash || row.revoked_at || new Date(row.expires_at) <= params.now || row.absolute_expires_at !== null && new Date(row.absolute_expires_at) <= params.now) {
2708
+ if (!row || row.refresh_token_hash !== null || row.audience !== params.audience || row.token_hash !== params.tokenHash || !isLiveSessionRow(row, params.now)) {
2709
+ return null;
2710
+ }
2711
+ return rowToSessionRecord(row);
2712
+ }
2713
+ async findValidByAccessTokenHash(params) {
2714
+ const row = await getSessionRowById(this.sql, params);
2715
+ if (!row || row.access_expires_at === null || row.token_hash !== params.tokenHash || new Date(row.access_expires_at) <= params.now || !isLiveSessionRow(row, params.now)) {
2674
2716
  return null;
2675
2717
  }
2676
2718
  return rowToSessionRecord(row);
2677
2719
  }
2720
+ async rotateRefreshToken(params) {
2721
+ return runPgTransaction(this.sql, async (tx) => {
2722
+ const row = await getSessionRowById(tx, params, { forUpdate: true });
2723
+ if (!row?.refresh_token_hash || !isLiveSessionRow(row, params.now)) {
2724
+ return { status: "invalid" };
2725
+ }
2726
+ const replacedRecently = row.previous_refresh_token_hash === params.refreshTokenHash && row.refreshed_at !== null && params.now.getTime() - new Date(row.refreshed_at).getTime() <= params.reuseGraceMs;
2727
+ if (row.refresh_token_hash !== params.refreshTokenHash && !replacedRecently) {
2728
+ if (row.previous_refresh_token_hash !== params.refreshTokenHash) {
2729
+ return { status: "invalid" };
2730
+ }
2731
+ await tx`
2732
+ UPDATE auth_sessions SET revoked_at = ${params.now}
2733
+ WHERE project_id = ${params.projectId} AND id = ${params.id}
2734
+ `;
2735
+ return { status: "reused" };
2736
+ }
2737
+ const [rotated] = await tx`
2738
+ UPDATE auth_sessions
2739
+ SET token_hash = ${params.next.tokenHash},
2740
+ access_expires_at = ${params.next.accessExpiresAt},
2741
+ previous_refresh_token_hash = refresh_token_hash,
2742
+ refresh_token_hash = ${params.next.refreshTokenHash},
2743
+ refreshed_at = ${params.now},
2744
+ last_seen_at = ${params.now},
2745
+ expires_at = GREATEST(
2746
+ expires_at,
2747
+ LEAST(${params.next.expiresAt}, COALESCE(absolute_expires_at, ${params.next.expiresAt}))
2748
+ )
2749
+ WHERE project_id = ${params.projectId} AND id = ${params.id}
2750
+ RETURNING *
2751
+ `;
2752
+ return rotated ? { status: "rotated", session: rowToSessionRecord(rotated) } : { status: "invalid" };
2753
+ });
2754
+ }
2678
2755
  async renewIfValid(params) {
2679
2756
  const [row] = await this.sql`
2680
2757
  UPDATE auth_sessions
@@ -2690,6 +2767,7 @@ class PgAuthSessionStore {
2690
2767
  AND id = ${params.id}
2691
2768
  AND audience = ${params.audience}
2692
2769
  AND token_hash = ${params.tokenHash}
2770
+ AND refresh_token_hash IS NULL
2693
2771
  AND revoked_at IS NULL
2694
2772
  AND expires_at > ${params.now}
2695
2773
  AND (absolute_expires_at IS NULL OR absolute_expires_at > ${params.now})
@@ -2737,6 +2815,9 @@ class PgAuthSessionStore {
2737
2815
  });
2738
2816
  }
2739
2817
  }
2818
+ function isLiveSessionRow(row, now) {
2819
+ return row.revoked_at === null && new Date(row.expires_at) > now && (row.absolute_expires_at === null || new Date(row.absolute_expires_at) > now);
2820
+ }
2740
2821
 
2741
2822
  // src/auth-storage/users.ts
2742
2823
  import { AuthStorageError as AuthStorageError10 } from "@sixb/core/storage";
@@ -2898,7 +2979,7 @@ class PgAuthStorage {
2898
2979
  throw new AuthStorageError11("missing_device_authorization", `[Sixb] Device authorization '${input.id}' not found for project '${input.projectId}'.`);
2899
2980
  }
2900
2981
  assertCompletableDeviceAuthorization(authorization, input);
2901
- const accessToken = await new PgAuthAccessTokenStore(tx).create(input.accessToken);
2982
+ const session = await createSession(tx, input.session);
2902
2983
  const [consumed] = await tx`
2903
2984
  UPDATE auth_device_authorizations
2904
2985
  SET status = 'consumed', consumed_at = ${input.completedAt}
@@ -2914,7 +2995,7 @@ class PgAuthStorage {
2914
2995
  status: "consumed",
2915
2996
  consumedAt: new Date(input.completedAt)
2916
2997
  },
2917
- accessToken
2998
+ session
2918
2999
  };
2919
3000
  });
2920
3001
  }
@@ -3376,7 +3457,7 @@ function assertCompletableDeviceAuthorization(authorization, input) {
3376
3457
  if (authorization.status === "denied") {
3377
3458
  throw new AuthStorageError11("device_authorization_denied", "[Sixb] Device authorization was denied.");
3378
3459
  }
3379
- if (authorization.status !== "approved" || authorization.expiresAt <= input.completedAt || !authorization.approvedUserId || !authorization.approvedSessionId || input.accessToken.projectId !== input.projectId || input.accessToken.subjectType !== "user" || input.accessToken.subjectId !== authorization.approvedUserId || input.accessToken.createdBySessionId !== authorization.approvedSessionId) {
3460
+ if (authorization.status !== "approved" || authorization.expiresAt <= input.completedAt || !authorization.approvedUserId || !authorization.approvedSessionId || input.session.projectId !== input.projectId || input.session.userId !== authorization.approvedUserId) {
3380
3461
  throw new AuthStorageError11("invalid_device_authorization", "[Sixb] Device authorization is invalid.");
3381
3462
  }
3382
3463
  }
@@ -7140,6 +7221,173 @@ ALTER TABLE projection_runs ADD COLUMN source_changes_read BIGINT
7140
7221
  CHECK (source_changes_read IS NULL OR source_changes_read >= 0);
7141
7222
  `;
7142
7223
 
7224
+ // src/migrations/041-agent-thread-workspaces.sql
7225
+ var _041_agent_thread_workspaces_default = `ALTER TABLE agent_threads ADD COLUMN sandbox_params JSONB
7226
+ CHECK (sandbox_params IS NULL OR jsonb_typeof(sandbox_params) = 'object');
7227
+ `;
7228
+
7229
+ // src/migrations/042-vector-profiles.sql
7230
+ var _042_vector_profiles_default = `-- Transactional derived state. Search extensions are not required for persistence.
7231
+ CREATE TABLE object_vectors (
7232
+ project_id TEXT NOT NULL,
7233
+ object_type_id TEXT NOT NULL,
7234
+ primary_id TEXT NOT NULL,
7235
+ profile TEXT NOT NULL CHECK (length(btrim(profile)) > 0),
7236
+ configuration TEXT NOT NULL,
7237
+ source TEXT[] NOT NULL,
7238
+ source_fingerprint TEXT NOT NULL,
7239
+ embedding REAL[] NOT NULL,
7240
+ last_commit_id TEXT NOT NULL,
7241
+ PRIMARY KEY (project_id, object_type_id, primary_id, profile),
7242
+ FOREIGN KEY (project_id, object_type_id, primary_id)
7243
+ REFERENCES objects (project_id, object_type_id, primary_id) ON DELETE CASCADE,
7244
+ FOREIGN KEY (project_id, last_commit_id)
7245
+ REFERENCES ontology_commits (project_id, id) DEFERRABLE INITIALLY DEFERRED,
7246
+ CONSTRAINT object_vectors_sources CHECK (
7247
+ cardinality(source) > 0 AND array_ndims(source) = 1 AND array_lower(source, 1) = 1
7248
+ AND array_position(source, NULL) IS NULL
7249
+ ),
7250
+ CONSTRAINT object_vectors_values CHECK (
7251
+ cardinality(embedding) BETWEEN 1 AND 16000
7252
+ AND array_ndims(embedding) = 1 AND array_lower(embedding, 1) = 1
7253
+ AND array_position(embedding, NULL) IS NULL
7254
+ AND '-Infinity'::real < ALL(embedding) AND 'Infinity'::real > ALL(embedding)
7255
+ AND 0::real <> ANY(embedding)
7256
+ )
7257
+ );
7258
+ `;
7259
+
7260
+ // src/migrations/043-vector-indexing.sql
7261
+ var _043_vector_indexing_default = `CREATE TABLE object_vector_indexing (
7262
+ project_id TEXT NOT NULL,
7263
+ id TEXT NOT NULL,
7264
+ object_type_id TEXT NOT NULL,
7265
+ primary_id TEXT NOT NULL,
7266
+ profile TEXT NOT NULL,
7267
+ request JSONB NOT NULL,
7268
+ status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'ready', 'failed')),
7269
+ available_at TIMESTAMPTZ NOT NULL,
7270
+ dispatch_at TIMESTAMPTZ NOT NULL,
7271
+ values_json JSONB,
7272
+ error TEXT,
7273
+ PRIMARY KEY (project_id, id),
7274
+ UNIQUE (project_id, object_type_id, primary_id, profile)
7275
+ );
7276
+ CREATE INDEX object_vector_indexing_due ON object_vector_indexing (project_id, dispatch_at, id)
7277
+ WHERE status <> 'failed';
7278
+
7279
+ ALTER TABLE executions DROP CONSTRAINT executions_authority_kernel_operation_check;
7280
+ ALTER TABLE executions ADD CONSTRAINT executions_authority_kernel_operation_check
7281
+ CHECK (authority_kernel_operation IS NULL OR authority_kernel_operation IN ('ontology.recover', 'ontology.indexVectors'));
7282
+ ALTER TABLE executions DROP CONSTRAINT executions_source_kind_check;
7283
+ ALTER TABLE executions ADD CONSTRAINT executions_source_kind_check
7284
+ CHECK (source_kind IN ('http', 'webhook', 'schedule', 'event', 'datasetVersion', 'execution', 'ontologyCommit'));
7285
+ `;
7286
+
7287
+ // src/migrations/044-vector-batching.sql
7288
+ var _044_vector_batching_default = `ALTER TABLE object_vector_indexing ADD COLUMN batch_id TEXT;
7289
+ CREATE INDEX object_vector_indexing_batch ON object_vector_indexing (project_id, batch_id, id)
7290
+ WHERE batch_id IS NOT NULL;
7291
+ `;
7292
+
7293
+ // src/migrations/045-agent-thread-sandbox-state.sql
7294
+ var _045_agent_thread_sandbox_state_default = `ALTER TABLE agent_threads ADD COLUMN sandbox_state JSONB
7295
+ CHECK (sandbox_state IS NULL OR jsonb_typeof(sandbox_state) = 'object');
7296
+ `;
7297
+
7298
+ // src/migrations/046-workflow-intervention-principals.sql
7299
+ var _046_workflow_intervention_principals_default = `-- Intervention actors were stored as { principalType, principalId }. Rewrite them to the
7300
+ -- canonical Principal shape { type, id } used by every other stored principal.
7301
+ UPDATE workflow_interventions
7302
+ SET submitted_by = jsonb_build_object(
7303
+ 'type', submitted_by->>'principalType',
7304
+ 'id', submitted_by->>'principalId'
7305
+ )
7306
+ WHERE submitted_by->>'principalType' IS NOT NULL;
7307
+
7308
+ UPDATE workflow_interventions
7309
+ SET cancelled_by = jsonb_build_object(
7310
+ 'type', cancelled_by->>'principalType',
7311
+ 'id', cancelled_by->>'principalId'
7312
+ )
7313
+ WHERE cancelled_by->>'principalType' IS NOT NULL;
7314
+ `;
7315
+
7316
+ // src/migrations/047-ontology-commit-attribution.sql
7317
+ var _047_ontology_commit_attribution_default = `-- Commits and their events name who requested the write and what ran it, replacing the
7318
+ -- authority-only \`actor\`. Both are copied from the commit's execution, which stays canonical and
7319
+ -- is immutable, so the backfill is exact.
7320
+ ALTER TABLE ontology_commits
7321
+ ADD COLUMN requested_by JSONB,
7322
+ ADD COLUMN executor JSONB;
7323
+
7324
+ UPDATE ontology_commits c SET
7325
+ requested_by = CASE
7326
+ WHEN e.requested_by_user_id IS NOT NULL
7327
+ THEN jsonb_build_object('type', 'user', 'id', e.requested_by_user_id)
7328
+ WHEN e.requested_by_service_account_id IS NOT NULL
7329
+ THEN jsonb_build_object('type', 'serviceAccount', 'id', e.requested_by_service_account_id)
7330
+ END,
7331
+ executor = CASE e.executor_kind
7332
+ WHEN 'request' THEN jsonb_build_object('type', 'request', 'requestId', e.executor_id)
7333
+ WHEN 'agent' THEN jsonb_build_object('type', 'agent', 'runId', e.executor_id)
7334
+ WHEN 'kernel' THEN jsonb_build_object(
7335
+ 'type', 'kernel',
7336
+ 'operation',
7337
+ CASE e.authority_kernel_operation
7338
+ WHEN 'ontology.recover'
7339
+ THEN jsonb_build_object('type', 'ontology.recover', 'recoveryId', e.executor_id)
7340
+ ELSE jsonb_build_object('type', 'ontology.indexVectors', 'indexingId', e.executor_id)
7341
+ END
7342
+ )
7343
+ ELSE jsonb_build_object(
7344
+ 'type', 'primitive',
7345
+ 'kind', e.executor_kind,
7346
+ 'id', e.authority_primitive_id,
7347
+ 'runId', e.executor_id
7348
+ )
7349
+ END
7350
+ FROM executions e
7351
+ WHERE e.project_id = c.project_id AND e.id = c.execution_id;
7352
+
7353
+ ALTER TABLE ontology_commits ALTER COLUMN executor SET NOT NULL;
7354
+
7355
+ -- Events not yet published would otherwise reach consumers in the retired shape.
7356
+ UPDATE ontology_outbox o SET envelope =
7357
+ (o.envelope - 'actor')
7358
+ || jsonb_build_object('executor', c.executor)
7359
+ || CASE
7360
+ WHEN c.requested_by IS NULL THEN '{}'::jsonb
7361
+ ELSE jsonb_build_object('requestedBy', c.requested_by)
7362
+ END
7363
+ FROM ontology_commits c
7364
+ WHERE c.project_id = o.project_id AND c.id = o.commit_id;
7365
+
7366
+ ALTER TABLE ontology_commits DROP COLUMN actor;
7367
+ `;
7368
+
7369
+ // src/migrations/049-native-sessions.sql
7370
+ var _049_native_sessions_default = `-- Native clients, such as the CLI, hold their session as bearer tokens: a
7371
+ -- short-lived access token, whose hash is \`token_hash\`, and a rotating refresh token. Browser
7372
+ -- sessions leave these columns NULL; \`refresh_token_hash IS NULL\` is what marks a cookie session.
7373
+ ALTER TABLE auth_sessions
7374
+ ADD COLUMN client_name TEXT,
7375
+ ADD COLUMN access_expires_at TIMESTAMPTZ,
7376
+ ADD COLUMN refresh_token_hash TEXT,
7377
+ ADD COLUMN previous_refresh_token_hash TEXT,
7378
+ ADD COLUMN refreshed_at TIMESTAMPTZ,
7379
+ ADD CONSTRAINT auth_sessions_bearer_state CHECK (
7380
+ (refresh_token_hash IS NULL) = (client_name IS NULL)
7381
+ AND (refresh_token_hash IS NULL) = (access_expires_at IS NULL)
7382
+ AND (refresh_token_hash IS NOT NULL OR previous_refresh_token_hash IS NULL)
7383
+ );
7384
+
7385
+ -- The device flow now starts a session instead of issuing a personal access token.
7386
+ ALTER TABLE auth_device_authorizations
7387
+ DROP COLUMN token_name,
7388
+ DROP COLUMN token_expires_at;
7389
+ `;
7390
+
7143
7391
  // src/migrations/037-execution-requester-groups.sql
7144
7392
  var _037_execution_requester_groups_default = `ALTER TABLE executions ADD COLUMN requester_group_ids JSONB NOT NULL DEFAULT '[]'::jsonb;
7145
7393
 
@@ -7178,6 +7426,43 @@ ALTER TABLE connector_authorization_attempts
7178
7426
  ALTER COLUMN code_verifier DROP NOT NULL;
7179
7427
  `;
7180
7428
 
7429
+ // src/migrations/048-file-upload-sessions.sql
7430
+ var _048_file_upload_sessions_default = `-- Durable staged-upload sessions, so an upload survives restarts and spans API replicas.
7431
+ CREATE TABLE file_upload_sessions (
7432
+ id TEXT PRIMARY KEY CHECK (length(trim(id)) > 0),
7433
+ project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
7434
+ owner_principal_type TEXT NOT NULL
7435
+ CHECK (owner_principal_type IN ('user', 'serviceAccount', 'system')),
7436
+ owner_principal_id TEXT NOT NULL CHECK (length(owner_principal_id) > 0),
7437
+ strategy TEXT NOT NULL CHECK (strategy IN ('server', 'direct-put', 'multipart')),
7438
+ status TEXT NOT NULL CHECK (status IN ('pending', 'completed', 'aborted')),
7439
+ file_name TEXT,
7440
+ media_type TEXT,
7441
+ logical_path TEXT,
7442
+ expected_size_bytes BIGINT CHECK (expected_size_bytes IS NULL OR expected_size_bytes >= 0),
7443
+ expected_digest TEXT,
7444
+ provider_upload JSONB CHECK (provider_upload IS NULL OR jsonb_typeof(provider_upload) = 'object'),
7445
+ signed_parts JSONB NOT NULL CHECK (jsonb_typeof(signed_parts) = 'array'),
7446
+ file_ref JSONB CHECK (file_ref IS NULL OR jsonb_typeof(file_ref) = 'object'),
7447
+ created_at TIMESTAMPTZ NOT NULL,
7448
+ expires_at TIMESTAMPTZ NOT NULL,
7449
+ completed_at TIMESTAMPTZ,
7450
+ aborted_at TIMESTAMPTZ,
7451
+ -- Derived by core (fileUploadSessionReapAt); NULL while an abandoned upload awaits its abort.
7452
+ reap_at TIMESTAMPTZ,
7453
+ CHECK ((status = 'completed') = (completed_at IS NOT NULL)),
7454
+ CHECK ((status = 'aborted') = (aborted_at IS NOT NULL))
7455
+ );
7456
+
7457
+ CREATE INDEX idx_file_upload_sessions_reap
7458
+ ON file_upload_sessions (reap_at)
7459
+ WHERE reap_at IS NOT NULL;
7460
+
7461
+ CREATE INDEX idx_file_upload_sessions_abandoned
7462
+ ON file_upload_sessions (project_id, expires_at)
7463
+ WHERE status = 'pending' AND provider_upload IS NOT NULL;
7464
+ `;
7465
+
7181
7466
  // src/migrations.ts
7182
7467
  var POSTGRES_STORAGE_ADAPTER_ID = "SixbPostgresStorage";
7183
7468
  function pgSql(id, sqlText) {
@@ -7392,17 +7677,38 @@ var postgresStorageMigrations = defineMigrations({
7392
7677
  pgSql("037-execution-requester-groups", _037_execution_requester_groups_default),
7393
7678
  pgSql("038-outbox-publication-order", _038_outbox_publication_order_default),
7394
7679
  pgSql("039-projection-source-roots", _039_projection_source_roots_default),
7395
- pgSql("040-connector-optional-pkce", _040_connector_optional_pkce_default)
7680
+ pgSql("040-connector-optional-pkce", _040_connector_optional_pkce_default),
7681
+ pgSql("041-agent-thread-workspaces", _041_agent_thread_workspaces_default),
7682
+ pgSql("042-vector-profiles", _042_vector_profiles_default),
7683
+ pgSql("043-vector-indexing", _043_vector_indexing_default),
7684
+ pgSql("044-vector-batching", _044_vector_batching_default),
7685
+ pgSql("045-agent-thread-sandbox-state", _045_agent_thread_sandbox_state_default),
7686
+ pgSql("046-workflow-intervention-principals", _046_workflow_intervention_principals_default),
7687
+ pgSql("047-ontology-commit-attribution", _047_ontology_commit_attribution_default),
7688
+ pgSql("048-file-upload-sessions", _048_file_upload_sessions_default),
7689
+ pgSql("049-native-sessions", _049_native_sessions_default)
7396
7690
  ]
7397
7691
  });
7398
7692
 
7693
+ // src/objects/storage.ts
7694
+ import { hasVectorProfile as hasVectorProfile3 } from "@sixb/core/internal/query";
7695
+
7399
7696
  // src/objects/query-compiler.ts
7400
7697
  import {
7401
7698
  ObjectQueryExecutionError
7402
7699
  } from "@sixb/core";
7700
+ import {
7701
+ hasVectorProfile,
7702
+ isVectorProfileQuery,
7703
+ normalizeVector,
7704
+ vectorSearchCandidateLimit
7705
+ } from "@sixb/core/internal/query";
7403
7706
  var PAGE_TOKEN_PREFIX = "keyset:";
7404
7707
  var DEFAULT_EXPANSION_FANOUT = 1000;
7405
7708
  function compilePgObjectQuery(projectId, query, options = {}) {
7709
+ if (hasVectorProfile(query) && !isVectorProfileQuery(query)) {
7710
+ throw new ObjectQueryExecutionError("unsupported_vector_composition", "Vector search supports one profile with filters before ranking and limit/project after.");
7711
+ }
7406
7712
  const source = options.source ?? DEFAULT_OBJECT_QUERY_SOURCE;
7407
7713
  const compiled = source.wrapQuery(compileObjectQueryInternal(projectId, query, {
7408
7714
  probeLimit: options.includeTotal === false,
@@ -7412,6 +7718,7 @@ function compilePgObjectQuery(projectId, query, options = {}) {
7412
7718
  ...compiled,
7413
7719
  sql: numberPlaceholders(compiled.sql),
7414
7720
  totalSql: numberPlaceholders(compiled.totalSql),
7721
+ vectorProbe: compiled.vectorProbe ? { ...compiled.vectorProbe, sql: numberPlaceholders(compiled.vectorProbe.sql) } : undefined,
7415
7722
  hasMoreProbe: compiled.hasMoreProbe ? {
7416
7723
  ...compiled.hasMoreProbe,
7417
7724
  sql: numberPlaceholders(compiled.hasMoreProbe.sql)
@@ -7489,8 +7796,56 @@ function compileObjectQueryInternal(projectId, query, ctx) {
7489
7796
  case "expand":
7490
7797
  return compileExpand(projectId, query.input, query.expansions, ctx);
7491
7798
  case "vector":
7492
- throw new Error(`[SixbPg] PostgreSQL object storage does not support query node '${query.kind}'`);
7493
- }
7799
+ return compileVector(projectId, query, ctx);
7800
+ }
7801
+ }
7802
+ function compileVector(projectId, query, ctx) {
7803
+ 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) {
7804
+ throw new ObjectQueryExecutionError("invalid_vector_query", "A validated named vector profile is required.");
7805
+ }
7806
+ const input = compileObjectQueryInternal(projectId, query.input, exactContext(ctx));
7807
+ const permissions = ctx.source.objectPropertyPermissionsTable;
7808
+ const permissionJoin = permissions ? `JOIN (
7809
+ SELECT project_id, object_type_id, primary_id, array_agg(property_id) AS property_ids
7810
+ FROM ${permissions}
7811
+ GROUP BY project_id, object_type_id, primary_id
7812
+ ) AS vector_permissions ON vector_permissions.project_id = vectors.project_id
7813
+ AND vector_permissions.object_type_id = vectors.object_type_id
7814
+ AND vector_permissions.primary_id = vectors.primary_id` : "";
7815
+ const authorized = permissions ? `AND vectors.source <@ vector_permissions.property_ids` : "";
7816
+ const candidates = `FROM (${input.sql}) AS input
7817
+ JOIN object_vectors AS vectors ON vectors.project_id = input.project_id
7818
+ AND vectors.object_type_id = input.object_type_id AND vectors.primary_id = input.primary_id
7819
+ ${permissionJoin}
7820
+ WHERE vectors.profile = ? AND vectors.configuration = ?
7821
+ AND cardinality(vectors.embedding) = ? ${authorized}`;
7822
+ const candidateArgs = [...input.args, query.profile, query.configuration, query.vector.length];
7823
+ const limit = vectorSearchCandidateLimit(query.vector.length);
7824
+ const values = normalizeVector(query.vector, query.vector.length);
7825
+ const order = compileOrder([
7826
+ { kind: "column", column: "_vector_score", direction: "desc" },
7827
+ ...identityOrderFields().map((field) => ({ ...field, binary: true }))
7828
+ ]);
7829
+ const sql = `SELECT * FROM (SELECT input.*, 1 - (vectors.embedding::public.vector OPERATOR(public.<=>) ?::public.vector) AS _vector_score
7830
+ ${candidates}) AS ranked ORDER BY ${order.sql} LIMIT ?`;
7831
+ const args = [JSON.stringify(values), ...candidateArgs, query.k];
7832
+ return {
7833
+ sql,
7834
+ args,
7835
+ totalSql: `SELECT COUNT(*)::bigint AS total FROM (SELECT 1 ${candidates} LIMIT ?) AS eligible`,
7836
+ totalArgs: [...candidateArgs, query.k],
7837
+ order,
7838
+ vectorProbe: {
7839
+ sql: `SELECT COUNT(*)::bigint AS total FROM (SELECT 1 ${candidates} LIMIT ?) AS candidates`,
7840
+ args: [...candidateArgs, limit + 1],
7841
+ limit
7842
+ },
7843
+ hasMore: () => false,
7844
+ trimRows: identityRows,
7845
+ nextPageToken: () => {
7846
+ return;
7847
+ }
7848
+ };
7494
7849
  }
7495
7850
  function compileStart(projectId, query, ctx) {
7496
7851
  if (query.includeSubtypes === true) {
@@ -7578,6 +7933,7 @@ function compileWhere(projectId, inputQuery, predicate, ctx) {
7578
7933
  WHERE ${predicate.sql}
7579
7934
  `,
7580
7935
  totalArgs: [...input.args, ...predicate.args],
7936
+ vectorProbe: input.vectorProbe,
7581
7937
  order: input.order,
7582
7938
  hasMore: () => false,
7583
7939
  trimRows: identityRows,
@@ -7604,7 +7960,7 @@ function compileSort(projectId, inputQuery, fields, ctx) {
7604
7960
  input.created_at,
7605
7961
  input.updated_at,
7606
7962
  input.version,
7607
- input.last_commit_id
7963
+ input.last_commit_id${input.vectorProbe ? ", input._vector_score" : ""}
7608
7964
  FROM (${input.sql}) AS input
7609
7965
  ORDER BY ${order.sql}
7610
7966
  `,
@@ -7635,6 +7991,7 @@ function compileLimit(projectId, inputQuery, rawLimit, ctx) {
7635
7991
  FROM (${input.sql}) AS input
7636
7992
  `,
7637
7993
  totalArgs: input.args,
7994
+ vectorProbe: input.vectorProbe,
7638
7995
  order: input.order,
7639
7996
  hasMore: (rowCount, total) => total === undefined ? ctx.probeLimit && rowCount > limit : limit < total,
7640
7997
  trimRows: ctx.probeLimit ? (rows) => rows.slice(0, limit) : identityRows,
@@ -7663,6 +8020,7 @@ function compilePage(projectId, inputQuery, rawPageSize, pageToken, ctx) {
7663
8020
  FROM (${input.sql}) AS input
7664
8021
  `,
7665
8022
  totalArgs: input.args,
8023
+ vectorProbe: input.vectorProbe,
7666
8024
  order: input.order,
7667
8025
  hasMore: (rowCount) => rowCount > pageSize,
7668
8026
  trimRows: (rows) => rows.slice(0, pageSize),
@@ -7739,6 +8097,7 @@ function compileExpand(projectId, inputQuery, expansions, ctx) {
7739
8097
  args: [...expand.args, ...input.args, ...inputOrder.args],
7740
8098
  totalSql: input.totalSql,
7741
8099
  totalArgs: input.totalArgs,
8100
+ vectorProbe: input.vectorProbe,
7742
8101
  order: input.order,
7743
8102
  hasMoreProbe: input.hasMoreProbe,
7744
8103
  hasMore: input.hasMore,
@@ -7904,13 +8263,14 @@ function compileProject(projectId, inputQuery, properties, ctx) {
7904
8263
  input.created_at,
7905
8264
  input.updated_at,
7906
8265
  input.version,
7907
- input.last_commit_id
8266
+ input.last_commit_id${input.vectorProbe ? ", input._vector_score" : ""}
7908
8267
  FROM (${input.sql}) AS input
7909
8268
  ORDER BY ${inputOrder.sql}
7910
8269
  `,
7911
8270
  args: [...projection.args, ...input.args, ...inputOrder.args],
7912
8271
  totalSql: input.totalSql,
7913
8272
  totalArgs: input.totalArgs,
8273
+ vectorProbe: input.vectorProbe,
7914
8274
  order: outputOrder,
7915
8275
  hasMoreProbe: input.hasMoreProbe,
7916
8276
  hasMore: input.hasMore,
@@ -7941,7 +8301,7 @@ function compileAggregateSource(projectId, query, source) {
7941
8301
  case "page":
7942
8302
  return compileRowQueryAggregateSource(projectId, query, source);
7943
8303
  case "vector":
7944
- throw new Error(`[SixbPg] PostgreSQL object storage does not support query node '${query.kind}'`);
8304
+ return compileRowQueryAggregateSource(projectId, query, source);
7945
8305
  }
7946
8306
  }
7947
8307
  function compileAggregateStart(projectId, query, source) {
@@ -8310,7 +8670,7 @@ function compileOrder(fields, qualifier, propertyColumn = "properties") {
8310
8670
  const args = [];
8311
8671
  for (const field of fields) {
8312
8672
  if (field.kind === "column") {
8313
- clauses.push(`${columnExpression(field.column, qualifier)} ${field.direction.toUpperCase()}`);
8673
+ clauses.push(`${columnExpression(field.column, qualifier)}${field.binary ? ' COLLATE "C"' : ""} ${field.direction.toUpperCase()}`);
8314
8674
  continue;
8315
8675
  }
8316
8676
  const direction = field.direction === "desc" ? "DESC" : "ASC";
@@ -8519,6 +8879,8 @@ function isPlainObject(value) {
8519
8879
  }
8520
8880
 
8521
8881
  // src/objects/reader.ts
8882
+ import { ObjectQueryExecutionError as ObjectQueryExecutionError2 } from "@sixb/core";
8883
+ import { hasVectorProfile as hasVectorProfile2 } from "@sixb/core/internal/query";
8522
8884
  import { linkBatchKey, objectBatchKey } from "@sixb/core/storage";
8523
8885
 
8524
8886
  // src/objects/rows.ts
@@ -8535,7 +8897,10 @@ function rowToObject(row) {
8535
8897
  };
8536
8898
  }
8537
8899
  function queryRowToObject(row) {
8538
- const base = rowToObject(row);
8900
+ const base = {
8901
+ ...rowToObject(row),
8902
+ ...row._vector_score === undefined ? {} : { score: row._vector_score }
8903
+ };
8539
8904
  const links = reviveExpandedLinks(row._expand);
8540
8905
  return links ? { ...base, links } : base;
8541
8906
  }
@@ -8610,7 +8975,23 @@ class PgObjectReader {
8610
8975
  this.sql = sql;
8611
8976
  this.source = source;
8612
8977
  }
8978
+ async assertVectorSearch(params) {
8979
+ if (!hasVectorProfile2(params.query))
8980
+ return;
8981
+ const compiled = compilePgObjectQuery(params.projectId, params.query, { source: this.source });
8982
+ if (!compiled.vectorProbe)
8983
+ return;
8984
+ 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");
8985
+ if (!extensions[0]?.ready)
8986
+ throw new ObjectQueryExecutionError2("vector_extension_missing", "PostgreSQL vector search requires pgvector installed in schema public (CREATE EXTENSION vector WITH SCHEMA public).");
8987
+ const probe = compiled.vectorProbe;
8988
+ const [row] = await this.sql.unsafe(probe.sql, probe.args);
8989
+ if (Number(row?.total ?? 0) > probe.limit) {
8990
+ throw new ObjectQueryExecutionError2("vector_work_limit_exceeded", `Exact vector search supports at most ${probe.limit} eligible vectors at this dimension. Narrow the query filters.`);
8991
+ }
8992
+ }
8613
8993
  async queryObjects(params) {
8994
+ await this.assertVectorSearch(params);
8614
8995
  const sql = this.sql;
8615
8996
  const source = this.source;
8616
8997
  const compiled = compilePgObjectQuery(params.projectId, params.query, {
@@ -8629,6 +9010,7 @@ class PgObjectReader {
8629
9010
  };
8630
9011
  }
8631
9012
  async countObjects(params) {
9013
+ await this.assertVectorSearch(params);
8632
9014
  const sql = this.sql;
8633
9015
  const source = this.source;
8634
9016
  const compiled = compilePgObjectCountQuery(params.projectId, stripOuterRowShape(params.query), {
@@ -8638,6 +9020,7 @@ class PgObjectReader {
8638
9020
  return { count: Number(row?.count ?? 0) };
8639
9021
  }
8640
9022
  async existsObjects(params) {
9023
+ await this.assertVectorSearch(params);
8641
9024
  const sql = this.sql;
8642
9025
  const source = this.source;
8643
9026
  const compiled = compilePgObjectExistsQuery(params.projectId, stripOuterRowShape(params.query), { source });
@@ -8645,6 +9028,7 @@ class PgObjectReader {
8645
9028
  return { exists: row !== undefined };
8646
9029
  }
8647
9030
  async facetObjects(params) {
9031
+ await this.assertVectorSearch(params);
8648
9032
  const sql = this.sql;
8649
9033
  const source = this.source;
8650
9034
  const facets = [];
@@ -9032,6 +9416,7 @@ ${sql}`,
9032
9416
  wrapQuery: (query) => ({
9033
9417
  ...query,
9034
9418
  ...wrapStatement(query.sql, query.args),
9419
+ vectorProbe: query.vectorProbe ? { ...query.vectorProbe, ...wrapStatement(query.vectorProbe.sql, query.vectorProbe.args) } : undefined,
9035
9420
  totalSql: `${compiled.sql}
9036
9421
  ${query.totalSql}`,
9037
9422
  totalArgs: [...compiled.args, ...query.totalArgs],
@@ -9153,32 +9538,29 @@ function compileSelectedReadScopeCte(scopeJson, projectId) {
9153
9538
  SELECT DISTINCT project_id, object_type_id, primary_id
9154
9539
  FROM _sixb_scope_reachable
9155
9540
  ),
9541
+ _sixb_scope_object_grants AS (
9542
+ SELECT reachable.project_id, reachable.object_type_id, reachable.primary_id,
9543
+ array_agg(DISTINCT selected_property.value) FILTER (WHERE selected_property.value IS NOT NULL) AS property_ids
9544
+ FROM _sixb_scope_reachable AS reachable
9545
+ LEFT JOIN _sixb_scope_object_selections AS selection
9546
+ ON selection.node_id = reachable.node_id
9547
+ AND selection.object_type_id = reachable.object_type_id
9548
+ LEFT JOIN LATERAL jsonb_array_elements_text(selection.property_ids) AS selected_property(value) ON true
9549
+ GROUP BY reachable.project_id, reachable.object_type_id, reachable.primary_id
9550
+ ),
9156
9551
  _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
9552
+ SELECT stored.project_id, stored.object_type_id, stored.primary_id,
9553
+ COALESCE((
9554
+ SELECT jsonb_object_agg(property.key, property.value)
9555
+ FROM jsonb_each(stored.properties) AS property(key, value)
9556
+ WHERE property.key = ANY(grants.property_ids)
9557
+ ), '{}'::jsonb) AS properties,
9558
+ stored.created_at, stored.updated_at, stored.version, stored.last_commit_id
9559
+ FROM _sixb_scope_object_grants AS grants
9560
+ JOIN objects AS stored
9561
+ ON stored.project_id = grants.project_id
9562
+ AND stored.object_type_id = grants.object_type_id
9563
+ AND stored.primary_id = grants.primary_id
9182
9564
  ),
9183
9565
  _sixb_scope_reached_links(
9184
9566
  project_id,
@@ -9385,6 +9767,7 @@ var PG_OBJECT_QUERY_CAPABILITIES = {
9385
9767
  refs: true,
9386
9768
  filter: true,
9387
9769
  text: true,
9770
+ vector: true,
9388
9771
  sort: true,
9389
9772
  limit: true,
9390
9773
  page: true,
@@ -9427,7 +9810,8 @@ var PG_OBJECT_QUERY_CAPABILITIES = {
9427
9810
  double: { equality: true, ordering: true },
9428
9811
  decimal: { equality: true, ordering: true },
9429
9812
  date: { equality: true, ordering: true },
9430
- timestamp: { equality: true, ordering: true }
9813
+ timestamp: { equality: true, ordering: true },
9814
+ userRef: { equality: true }
9431
9815
  },
9432
9816
  limits: {
9433
9817
  totalCount: true,
@@ -9437,7 +9821,7 @@ var PG_OBJECT_QUERY_CAPABILITIES = {
9437
9821
  "PostgreSQL object query pushdown supports start/refs/filter/text/sort/limit/page/traverse/set/project/expand over JSONB properties and object links.",
9438
9822
  "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
9823
  "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."
9824
+ "Relevance sorting and unresolved start.includeSubtypes remain planner fallback or rejection cases."
9441
9825
  ]
9442
9826
  };
9443
9827
 
@@ -9455,15 +9839,23 @@ class PgObjectStorage {
9455
9839
  return createPgSelectedReader(this.sql, params, () => this.queryCapabilities());
9456
9840
  }
9457
9841
  async queryObjects(params) {
9842
+ if (hasVectorProfile3(params.query))
9843
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).queryObjects(params));
9458
9844
  return this.reader.queryObjects(params);
9459
9845
  }
9460
9846
  async countObjects(params) {
9847
+ if (hasVectorProfile3(params.query))
9848
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).countObjects(params));
9461
9849
  return this.reader.countObjects(params);
9462
9850
  }
9463
9851
  async existsObjects(params) {
9852
+ if (hasVectorProfile3(params.query))
9853
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).existsObjects(params));
9464
9854
  return this.reader.existsObjects(params);
9465
9855
  }
9466
9856
  async facetObjects(params) {
9857
+ if (hasVectorProfile3(params.query))
9858
+ return runPgRepeatableReadTransaction(this.sql, (tx) => new PgObjectReader(tx, DEFAULT_OBJECT_QUERY_SOURCE).facetObjects(params));
9467
9859
  return this.reader.facetObjects(params);
9468
9860
  }
9469
9861
  async getByPrimaryId(params) {
@@ -9622,9 +10014,10 @@ function commitRecord(row) {
9622
10014
  requestHash: row.request_hash,
9623
10015
  executionId: row.execution_id,
9624
10016
  origin: structuredClone(row.origin),
9625
- ...row.actor === null ? {} : {
9626
- actor: structuredClone(row.actor)
10017
+ ...row.requested_by === null ? {} : {
10018
+ requestedBy: structuredClone(row.requested_by)
9627
10019
  },
10020
+ executor: structuredClone(row.executor),
9628
10021
  ontologyRevision: row.ontology_revision,
9629
10022
  ...row.projection_revision === null ? {} : { projectionRevision: row.projection_revision },
9630
10023
  ...row.ownership_hash === null ? {} : { ownershipHash: row.ownership_hash },
@@ -9766,12 +10159,16 @@ import {
9766
10159
  linkRefSortKey,
9767
10160
  linkScopeSortKey as linkScopeSortKey2,
9768
10161
  MaterializationConflictError as MaterializationConflictError6,
10162
+ MaterializationValidationError as MaterializationValidationError2,
9769
10163
  objectRefKey as objectRefKey3,
9770
10164
  objectRefSortKey,
9771
10165
  telemetryPointKey as telemetryPointKey3,
9772
10166
  telemetryPointSortKey as telemetryPointSortKey2
9773
10167
  } from "@sixb/core/internal/materialization";
9774
10168
  import {
10169
+ assertExpectedLinkRevision,
10170
+ assertExpectedLinkScopeRevision,
10171
+ assertExpectedObjectRevision,
9775
10172
  assertMaterializationFinalizationCorrelation,
9776
10173
  assertMaterializationHeader,
9777
10174
  assertMaterializationLaneCompletion,
@@ -11692,6 +12089,12 @@ class PgOntologyMaterializationStorage {
11692
12089
  this.sessions = new PgMaterializationSessions(sql, context);
11693
12090
  this.writer = new PgMaterializationWriter(sql);
11694
12091
  }
12092
+ assertVectorSession(session, projectId, commitId) {
12093
+ const active = this.sessions.require(session);
12094
+ if (active.header.commit.projectId !== projectId || commitId !== undefined && active.header.commit.id !== commitId) {
12095
+ throw new MaterializationValidationError2("Vector mutation does not belong to this materialization session.");
12096
+ }
12097
+ }
11695
12098
  async begin(input) {
11696
12099
  assertMaterializationHeader(input);
11697
12100
  const session = await this.sessions.create(input);
@@ -11702,17 +12105,15 @@ class PgOntologyMaterializationStorage {
11702
12105
  await this.assertSources(input.expected.sources, input.commit.projectId);
11703
12106
  const objectRevisions = await reader.effectiveObjectRevisions(input.expected.objects.map((expected) => expected.ref), true);
11704
12107
  for (const expected of input.expected.objects) {
11705
- this.assertObject(objectRevisions.get(objectRefKey3(expected.ref)) ?? null, expected);
12108
+ assertExpectedObjectRevision(objectRevisions.get(objectRefKey3(expected.ref)) ?? null, expected);
11706
12109
  }
11707
12110
  const linkRevisions = await reader.effectiveLinkLastCommits(input.expected.links.map((expected) => expected.ref), true);
11708
12111
  for (const expected of input.expected.links) {
11709
- this.assertLink(linkRevisions.get(linkRefKey3(expected.ref)), expected);
12112
+ assertExpectedLinkRevision(linkRevisions.get(linkRefKey3(expected.ref)), expected);
11710
12113
  }
11711
12114
  const linkScopeRevisions = await reader.linkScopeRevisions(input.expected.linkScopes);
11712
12115
  for (const [index, expected] of input.expected.linkScopes.entries()) {
11713
- if (linkScopeRevisions[index]?.fingerprint !== expected.fingerprint) {
11714
- throw new MaterializationConflictError6("effective-state", `Expected link scope changed for ${expected.source.objectTypeId}:${expected.source.primaryId}.${expected.linkId}.`);
11715
- }
12116
+ assertExpectedLinkScopeRevision(linkScopeRevisions[index]?.fingerprint, expected);
11716
12117
  }
11717
12118
  const points = await reader.exactPoints(input.expected.points, true);
11718
12119
  const pointRevisions = new Map(points.map((point) => [telemetryPointKey3(point.series, point.at), point.lastCommitId]));
@@ -11972,28 +12373,6 @@ class PgOntologyMaterializationStorage {
11972
12373
  throw new MaterializationConflictError6("projection-fence", `Source '${expected.source.projectionId}' changed.`);
11973
12374
  }
11974
12375
  }
11975
- assertObject(row, expected) {
11976
- if (!expected.exists) {
11977
- if (row) {
11978
- throw new MaterializationConflictError6("effective-state", `Expected object ${objectRefKey3(expected.ref)} to be absent.`);
11979
- }
11980
- return;
11981
- }
11982
- if (!row || row.version !== expected.version || row.lastCommitId !== expected.lastCommitId) {
11983
- throw new MaterializationConflictError6("effective-state", `Expected object ${objectRefKey3(expected.ref)} changed.`);
11984
- }
11985
- }
11986
- assertLink(lastCommitId, expected) {
11987
- if (!expected.exists) {
11988
- if (lastCommitId !== undefined) {
11989
- throw new MaterializationConflictError6("effective-state", `Expected link ${linkRefKey3(expected.ref)} to be absent.`);
11990
- }
11991
- return;
11992
- }
11993
- if (lastCommitId === undefined || lastCommitId !== expected.lastCommitId) {
11994
- throw new MaterializationConflictError6("effective-state", `Expected link ${linkRefKey3(expected.ref)} changed.`);
11995
- }
11996
- }
11997
12376
  async assertFinalization(session, input) {
11998
12377
  const { commit } = session.header;
11999
12378
  const { result } = input.finalization;
@@ -12229,18 +12608,19 @@ class PgOntologyMaterializationStorage {
12229
12608
  async insertCommit(header, input) {
12230
12609
  const { commit } = header;
12231
12610
  const origin = originColumns(commit.origin);
12232
- const actor = commit.actor === undefined ? null : jsonParameter2(this.sql, commit.actor);
12611
+ const requestedBy = commit.requestedBy === undefined ? null : jsonParameter2(this.sql, commit.requestedBy);
12233
12612
  const rows = await this.sql`
12234
12613
  INSERT INTO ontology_commits (
12235
12614
  project_id, id, idempotency_key, request_hash, execution_id,
12236
- origin_kind, origin_run_id, origin_batch_ordinal, origin, actor,
12615
+ origin_kind, origin_run_id, origin_batch_ordinal, origin, requested_by, executor,
12237
12616
  ontology_revision, projection_revision, ownership_hash,
12238
12617
  intent, result, committed_at
12239
12618
  ) VALUES (
12240
12619
  ${commit.projectId}, ${commit.id}, ${commit.idempotencyKey}, ${commit.requestHash},
12241
12620
  ${commit.executionId},
12242
12621
  ${origin.kind}, ${origin.runId}, ${origin.batchOrdinal},
12243
- ${jsonParameter2(this.sql, commit.origin)}, ${actor}, ${commit.ontologyRevision},
12622
+ ${jsonParameter2(this.sql, commit.origin)}, ${requestedBy},
12623
+ ${jsonParameter2(this.sql, commit.executor)}, ${commit.ontologyRevision},
12244
12624
  ${commit.projectionRevision ?? null}, ${commit.ownershipHash ?? null},
12245
12625
  ${jsonParameter2(this.sql, commit.intent)},
12246
12626
  ${jsonParameter2(this.sql, input.finalization.result)}, ${commit.committedAt}
@@ -12299,7 +12679,7 @@ function databaseCount(value) {
12299
12679
  import { serializeSixbFailure as serializeSixbFailure2 } from "@sixb/core/internal/errors";
12300
12680
  import {
12301
12681
  MaterializationConflictError as MaterializationConflictError7,
12302
- MaterializationValidationError as MaterializationValidationError2
12682
+ MaterializationValidationError as MaterializationValidationError3
12303
12683
  } from "@sixb/core/internal/materialization";
12304
12684
  import { ONTOLOGY_OUTBOX_FAILURE_CODES as ONTOLOGY_OUTBOX_FAILURE_CODES2 } from "@sixb/core/storage";
12305
12685
  class PgOntologyOutboxStorage {
@@ -12315,7 +12695,7 @@ class PgOntologyOutboxStorage {
12315
12695
  const leaseExpiry = assertTimestamp(input.leaseExpiresAt, "Ontology outbox lease expiry");
12316
12696
  assertNonblank(input.leaseId, "Ontology outbox lease id");
12317
12697
  if (leaseExpiry <= now) {
12318
- throw new MaterializationValidationError2("Ontology outbox lease expiry must be later than the claim time.");
12698
+ throw new MaterializationValidationError3("Ontology outbox lease expiry must be later than the claim time.");
12319
12699
  }
12320
12700
  const rows = await sql`
12321
12701
  WITH candidates AS (
@@ -12466,7 +12846,7 @@ function validateLeaseIds(ids) {
12466
12846
  for (const id of ids) {
12467
12847
  assertNonblank(id, "Ontology outbox event id");
12468
12848
  if (seen.has(id)) {
12469
- throw new MaterializationValidationError2(`Ontology outbox lease batch repeats event '${id}'.`);
12849
+ throw new MaterializationValidationError3(`Ontology outbox lease batch repeats event '${id}'.`);
12470
12850
  }
12471
12851
  seen.add(id);
12472
12852
  }
@@ -12481,7 +12861,7 @@ function leaseConflict() {
12481
12861
  }
12482
12862
 
12483
12863
  // src/ontology-storage/sources.ts
12484
- import { MaterializationValidationError as MaterializationValidationError3 } from "@sixb/core/internal/materialization";
12864
+ import { MaterializationValidationError as MaterializationValidationError4 } from "@sixb/core/internal/materialization";
12485
12865
  import {
12486
12866
  assertSourceBeginInput as assertBeginInput,
12487
12867
  assertSourceExecutionIdentity as assertExecutionIdentity,
@@ -12641,7 +13021,7 @@ class PgOntologySourceStorage {
12641
13021
  throw sourceConflict(`Source materialization '${input.materializationId}' cannot transition from '${manifest.status}' to 'ready'.`);
12642
13022
  }
12643
13023
  if (input.readyAt < toIsoString2(manifest.created_at)) {
12644
- throw new MaterializationValidationError3("Source readyAt cannot precede source createdAt.");
13024
+ throw new MaterializationValidationError4("Source readyAt cannot precede source createdAt.");
12645
13025
  }
12646
13026
  if (input.assertionCount >= 1000 || input.rootCount >= 1000) {
12647
13027
  await sql`ANALYZE ontology_source_rows, ontology_source_roots`;
@@ -12739,7 +13119,7 @@ class PgOntologySourceStorage {
12739
13119
  }
12740
13120
  async transitionToAbandoned(sql, manifest, abandonedAt) {
12741
13121
  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.");
13122
+ throw new MaterializationValidationError4("Source abandonedAt cannot precede source creation or readiness.");
12743
13123
  }
12744
13124
  const rows = await sql`
12745
13125
  UPDATE ontology_sources
@@ -12777,7 +13157,7 @@ class PgOntologySourceStorage {
12777
13157
  const minimum = counts?.min_ordinal === null ? null : Number(counts?.min_ordinal);
12778
13158
  const maximum = counts?.max_ordinal === null ? null : Number(counts?.max_ordinal);
12779
13159
  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.");
13160
+ throw new MaterializationValidationError4("Source ready counts do not match the staged roots and assertions.");
12781
13161
  }
12782
13162
  await assertSourceRootCoverage(sql, manifest);
12783
13163
  const [invalid] = manifest.projection_kind === "link" ? await sql`
@@ -12813,7 +13193,7 @@ class PgOntologySourceStorage {
12813
13193
  LIMIT 1
12814
13194
  `;
12815
13195
  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.");
13196
+ 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
13197
  }
12818
13198
  }
12819
13199
  async findStageRows(sql, input, rows) {
@@ -12945,17 +13325,300 @@ function numberOrNull(value) {
12945
13325
  return value === null ? null : Number(value);
12946
13326
  }
12947
13327
 
13328
+ // src/ontology-storage/vector-indexing.ts
13329
+ import { parseSixbFailure as parseSixbFailure3 } from "@sixb/core/internal/errors";
13330
+ import {
13331
+ VECTOR_INDEXING_FAILURE_CODES
13332
+ } from "@sixb/core/storage";
13333
+ class PgVectorIndexingStorage {
13334
+ sql;
13335
+ run;
13336
+ assertSession;
13337
+ constructor(sql, run, assertSession) {
13338
+ this.sql = sql;
13339
+ this.run = run;
13340
+ this.assertSession = assertSession;
13341
+ }
13342
+ async schedule(input) {
13343
+ this.assertSession(input.session, input.projectId);
13344
+ if (input.deleted.length)
13345
+ await this.sql`
13346
+ DELETE FROM object_vector_indexing AS work
13347
+ USING jsonb_array_elements(${jsonParameter2(this.sql, input.deleted)}::jsonb) AS deleted(ref)
13348
+ WHERE work.project_id = ${input.projectId}
13349
+ AND work.object_type_id = deleted.ref->>'objectTypeId'
13350
+ AND work.primary_id = deleted.ref->>'primaryId'
13351
+ `;
13352
+ if (input.requests.length)
13353
+ await this.sql`
13354
+ INSERT INTO object_vector_indexing (
13355
+ project_id, id, object_type_id, primary_id, profile, batch_id, request, status, available_at, dispatch_at
13356
+ )
13357
+ SELECT ${input.projectId}, entry->>'id', entry#>>'{ref,objectTypeId}',
13358
+ entry#>>'{ref,primaryId}', entry->>'profile', entry->>'batchId', entry - 'batchId', 'pending',
13359
+ ${input.availableAt}::timestamptz, ${input.availableAt}::timestamptz
13360
+ FROM jsonb_array_elements(${jsonParameter2(this.sql, input.requests)}::jsonb) AS pending(entry)
13361
+ ON CONFLICT (project_id, object_type_id, primary_id, profile) DO UPDATE SET
13362
+ id = EXCLUDED.id, batch_id = EXCLUDED.batch_id, request = EXCLUDED.request, status = 'pending',
13363
+ available_at = EXCLUDED.available_at, dispatch_at = EXCLUDED.dispatch_at,
13364
+ values_json = NULL, error = NULL
13365
+ `;
13366
+ }
13367
+ async complete(input) {
13368
+ this.assertSession(input.session, input.projectId);
13369
+ if (!input.entries.length)
13370
+ return;
13371
+ await this.sql`
13372
+ DELETE FROM object_vector_indexing AS work
13373
+ USING jsonb_array_elements(${jsonParameter2(this.sql, input.entries)}::jsonb) AS completed(entry)
13374
+ WHERE work.project_id = ${input.projectId}
13375
+ AND work.object_type_id = entry#>>'{ref,objectTypeId}'
13376
+ AND work.primary_id = entry#>>'{ref,primaryId}'
13377
+ AND work.profile = entry->>'profile'
13378
+ AND work.request->>'configuration' = entry->>'configuration'
13379
+ AND work.request->>'sourceFingerprint' = entry->>'sourceFingerprint'
13380
+ `;
13381
+ }
13382
+ async dispatched(input) {
13383
+ if (!input.ids.length)
13384
+ return;
13385
+ await this.run(async (sql) => {
13386
+ await sql`UPDATE object_vector_indexing SET dispatch_at = ${input.nextDispatchAt} WHERE project_id = ${input.projectId} AND id = ANY(${sql.array([...input.ids])}::text[])`;
13387
+ });
13388
+ }
13389
+ async get(input) {
13390
+ return this.run(async (sql) => {
13391
+ 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}`;
13392
+ return rows[0] ? decode(rows[0]) : null;
13393
+ });
13394
+ }
13395
+ async getBatch(input) {
13396
+ return this.run(async (sql) => (await sql`
13397
+ SELECT batch_id, request, status, available_at, values_json, error
13398
+ FROM object_vector_indexing WHERE project_id = ${input.projectId}
13399
+ AND batch_id = ${input.batchId} ORDER BY id`).map(decode));
13400
+ }
13401
+ async getBatchMember(input) {
13402
+ return this.run(async (sql) => {
13403
+ const rows = await sql`
13404
+ SELECT batch_id, request, status, available_at, values_json, error
13405
+ FROM object_vector_indexing WHERE project_id = ${input.projectId}
13406
+ AND object_type_id = ${input.ref.objectTypeId} AND primary_id = ${input.ref.primaryId}
13407
+ AND profile = ${input.profile} AND batch_id = ${input.batchId}`;
13408
+ return rows[0] ? decode(rows[0]) : null;
13409
+ });
13410
+ }
13411
+ async updateBatch(input) {
13412
+ if (!input.updates.length)
13413
+ return true;
13414
+ return this.run(async (sql) => {
13415
+ const rows = await sql`
13416
+ WITH changes AS MATERIALIZED (
13417
+ SELECT item FROM jsonb_array_elements(${jsonParameter2(sql, input.updates)}::jsonb) AS entries(item)
13418
+ ), locked AS MATERIALIZED (
13419
+ SELECT work.id FROM object_vector_indexing AS work
13420
+ JOIN changes ON work.id = item->>'id' AND work.status = item->>'expectedStatus'
13421
+ WHERE work.project_id = ${input.projectId} ORDER BY work.id FOR UPDATE OF work
13422
+ )
13423
+ UPDATE object_vector_indexing AS work SET
13424
+ status = changes.item->>'status', available_at = (changes.item->>'availableAt')::timestamptz,
13425
+ values_json = COALESCE(changes.item->'values', work.values_json),
13426
+ error = (changes.item->'error')::text
13427
+ FROM changes, locked
13428
+ WHERE work.project_id = ${input.projectId} AND work.id = locked.id
13429
+ AND work.id = changes.item->>'id'
13430
+ AND (${!input.requireAll} OR (SELECT count(*) FROM locked) = ${input.updates.length})
13431
+ RETURNING work.id`;
13432
+ return !input.requireAll || rows.length === input.updates.length;
13433
+ });
13434
+ }
13435
+ async listDue(input) {
13436
+ 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));
13437
+ }
13438
+ async update(input) {
13439
+ return this.run(async (sql) => (await sql`
13440
+ UPDATE object_vector_indexing SET status = ${input.status}, available_at = ${input.availableAt},
13441
+ values_json = COALESCE(${input.values ? jsonParameter2(sql, input.values) : null}, values_json), error = ${input.error ? JSON.stringify(input.error) : null}
13442
+ WHERE project_id = ${input.projectId} AND id = ${input.id} AND status = ${input.expectedStatus} RETURNING id
13443
+ `).length === 1);
13444
+ }
13445
+ async remove(input) {
13446
+ await this.run(async (sql) => {
13447
+ await sql`DELETE FROM object_vector_indexing WHERE project_id = ${input.projectId} AND id = ${input.id}`;
13448
+ });
13449
+ }
13450
+ }
13451
+ function decode(row) {
13452
+ return {
13453
+ ...row.request,
13454
+ ...row.batch_id ? { batchId: row.batch_id } : {},
13455
+ status: row.status,
13456
+ availableAt: new Date(row.available_at).toISOString(),
13457
+ ...row.values_json ? { values: row.values_json } : {},
13458
+ ...row.error ? {
13459
+ error: parseSixbFailure3(row.error, VECTOR_INDEXING_FAILURE_CODES)
13460
+ } : {}
13461
+ };
13462
+ }
13463
+
13464
+ // src/ontology-storage/vectors.ts
13465
+ import { MaterializationConflictError as MaterializationConflictError8 } from "@sixb/core/internal/materialization";
13466
+ class PgOntologyVectorStorage {
13467
+ sql;
13468
+ runRootOperation;
13469
+ assertSession;
13470
+ constructor(sql, runRootOperation, assertSession) {
13471
+ this.sql = sql;
13472
+ this.runRootOperation = runRootOperation;
13473
+ this.assertSession = assertSession;
13474
+ }
13475
+ async list(input) {
13476
+ return this.runRootOperation(async (sql) => {
13477
+ const rows = await sql`
13478
+ SELECT profile, configuration, source, source_fingerprint, last_commit_id
13479
+ FROM object_vectors
13480
+ WHERE project_id = ${input.projectId}
13481
+ AND object_type_id = ${input.ref.objectTypeId} AND primary_id = ${input.ref.primaryId}
13482
+ ORDER BY profile
13483
+ `;
13484
+ return rows.map((row) => ({
13485
+ ref: { ...input.ref },
13486
+ profile: row.profile,
13487
+ configuration: row.configuration,
13488
+ source: row.source,
13489
+ sourceFingerprint: row.source_fingerprint,
13490
+ lastCommitId: row.last_commit_id
13491
+ }));
13492
+ });
13493
+ }
13494
+ async listBatch(input) {
13495
+ if (!input.refs.length)
13496
+ return [];
13497
+ return this.runRootOperation(async (sql) => {
13498
+ const rows = await sql`
13499
+ SELECT stored.object_type_id, stored.primary_id, stored.profile, stored.configuration,
13500
+ stored.source, stored.source_fingerprint, stored.last_commit_id
13501
+ FROM (SELECT DISTINCT ref->>'objectTypeId' AS object_type_id, ref->>'primaryId' AS primary_id
13502
+ FROM jsonb_array_elements(${jsonParameter2(sql, input.refs)}::jsonb) AS requested(ref)) AS requested
13503
+ JOIN object_vectors AS stored ON stored.project_id = ${input.projectId}
13504
+ AND stored.object_type_id = requested.object_type_id AND stored.primary_id = requested.primary_id
13505
+ ORDER BY stored.object_type_id, stored.primary_id, stored.profile
13506
+ `;
13507
+ return rows.map((row) => ({
13508
+ ref: { objectTypeId: row.object_type_id, primaryId: row.primary_id },
13509
+ profile: row.profile,
13510
+ configuration: row.configuration,
13511
+ source: row.source,
13512
+ sourceFingerprint: row.source_fingerprint,
13513
+ lastCommitId: row.last_commit_id
13514
+ }));
13515
+ });
13516
+ }
13517
+ async removeBatch(input) {
13518
+ this.assertSession(input.session, input.projectId);
13519
+ if (!input.entries.length)
13520
+ return;
13521
+ const removed = await this.sql`
13522
+ DELETE FROM object_vectors AS stored
13523
+ USING jsonb_array_elements(${jsonParameter2(this.sql, input.entries)}::jsonb) AS requested(entry)
13524
+ WHERE stored.project_id = ${input.projectId}
13525
+ AND stored.object_type_id = entry#>>'{ref,objectTypeId}'
13526
+ AND stored.primary_id = entry#>>'{ref,primaryId}' AND stored.profile = entry->>'profile'
13527
+ AND stored.last_commit_id = entry->>'expectedCommitId'
13528
+ RETURNING stored.profile
13529
+ `;
13530
+ if (removed.length !== input.entries.length)
13531
+ throw vectorConflict();
13532
+ }
13533
+ async write(input) {
13534
+ await this.writeBatch({ session: input.session, projectId: input.projectId, entries: [input] });
13535
+ }
13536
+ async writeBatch(input) {
13537
+ const entries = input.entries.map(({ value, expectedCommitId }) => {
13538
+ this.assertSession(input.session, input.projectId, value.lastCommitId);
13539
+ return {
13540
+ object_type_id: value.ref.objectTypeId,
13541
+ primary_id: value.ref.primaryId,
13542
+ profile: value.profile,
13543
+ configuration: value.configuration,
13544
+ source: [...value.source],
13545
+ source_fingerprint: value.sourceFingerprint,
13546
+ embedding: [...value.values],
13547
+ last_commit_id: value.lastCommitId,
13548
+ expected_commit_id: expectedCommitId
13549
+ };
13550
+ });
13551
+ const inserts = entries.filter((entry) => entry.expected_commit_id === null);
13552
+ const updates = entries.filter((entry) => entry.expected_commit_id !== null);
13553
+ if (inserts.length) {
13554
+ const rows = await this.sql`
13555
+ INSERT INTO object_vectors (
13556
+ project_id, object_type_id, primary_id, profile, configuration,
13557
+ source, source_fingerprint, embedding, last_commit_id
13558
+ )
13559
+ SELECT ${input.projectId}, object_type_id, primary_id, profile, configuration,
13560
+ source, source_fingerprint, embedding, last_commit_id
13561
+ FROM jsonb_to_recordset(${jsonParameter2(this.sql, inserts)}::jsonb) AS incoming(
13562
+ object_type_id text, primary_id text, profile text, configuration text,
13563
+ source text[], source_fingerprint text, embedding real[], last_commit_id text
13564
+ )
13565
+ ORDER BY object_type_id, primary_id, profile
13566
+ ON CONFLICT (project_id, object_type_id, primary_id, profile) DO NOTHING
13567
+ RETURNING profile`;
13568
+ if (rows.length !== inserts.length)
13569
+ throw vectorConflict();
13570
+ }
13571
+ if (updates.length) {
13572
+ const rows = await this.sql`
13573
+ UPDATE object_vectors AS stored SET
13574
+ configuration = incoming.configuration, source = incoming.source,
13575
+ source_fingerprint = incoming.source_fingerprint, embedding = incoming.embedding,
13576
+ last_commit_id = incoming.last_commit_id
13577
+ FROM jsonb_to_recordset(${jsonParameter2(this.sql, updates)}::jsonb) AS incoming(
13578
+ object_type_id text, primary_id text, profile text, configuration text,
13579
+ source text[], source_fingerprint text, embedding real[], last_commit_id text,
13580
+ expected_commit_id text
13581
+ )
13582
+ WHERE stored.project_id = ${input.projectId}
13583
+ AND stored.object_type_id = incoming.object_type_id AND stored.primary_id = incoming.primary_id
13584
+ AND stored.profile = incoming.profile AND stored.last_commit_id = incoming.expected_commit_id
13585
+ RETURNING stored.profile`;
13586
+ if (rows.length !== updates.length)
13587
+ throw vectorConflict();
13588
+ }
13589
+ }
13590
+ async remove(input) {
13591
+ this.assertSession(input.session, input.projectId);
13592
+ const rows = await this.sql`
13593
+ DELETE FROM object_vectors
13594
+ WHERE project_id = ${input.projectId}
13595
+ AND object_type_id = ${input.ref.objectTypeId} AND primary_id = ${input.ref.primaryId}
13596
+ AND profile = ${input.profile} AND last_commit_id = ${input.expectedCommitId}
13597
+ RETURNING profile
13598
+ `;
13599
+ if (rows.length !== 1)
13600
+ throw vectorConflict();
13601
+ }
13602
+ }
13603
+ function vectorConflict() {
13604
+ return new MaterializationConflictError8("effective-state", "Vector changed since it was prepared.");
13605
+ }
13606
+
12948
13607
  // src/ontology-storage/index.ts
12949
13608
  class PgOntologyStorage {
12950
13609
  commits;
12951
13610
  sources;
12952
13611
  materializations;
12953
13612
  outbox;
13613
+ vectorIndexing;
13614
+ vectors;
12954
13615
  constructor(input) {
12955
13616
  this.commits = new PgOntologyCommitStorage(input.runRootOperation);
12956
13617
  this.sources = new PgOntologySourceStorage(input.runRootOperation);
12957
13618
  this.materializations = new PgOntologyMaterializationStorage(input.sql, input.transactionContext);
12958
13619
  this.outbox = new PgOntologyOutboxStorage(input.runRootOperation);
13620
+ this.vectorIndexing = new PgVectorIndexingStorage(input.sql, input.runRootOperation, (session, projectId) => this.materializations.assertVectorSession(session, projectId));
13621
+ this.vectors = new PgOntologyVectorStorage(input.sql, input.runRootOperation, (session, projectId, commitId) => this.materializations.assertVectorSession(session, projectId, commitId));
12959
13622
  }
12960
13623
  deactivateSessions() {
12961
13624
  this.materializations.deactivateSessions();
@@ -13925,6 +14588,8 @@ class PgAiCostStorage {
13925
14588
  COUNT(*)::text AS model_call_count,
13926
14589
  COUNT(*) FILTER (WHERE reporting_status <> 'unavailable')::text
13927
14590
  AS reported_usage_call_count,
14591
+ COUNT(*) FILTER (WHERE input_tokens IS NOT NULL AND output_tokens IS NOT NULL)::text
14592
+ AS complete_usage_call_count,
13928
14593
  COUNT(input_tokens)::text AS input_tokens_count,
13929
14594
  SUM(input_tokens)::text AS input_tokens_sum,
13930
14595
  COUNT(output_tokens)::text AS output_tokens_count,
@@ -14066,6 +14731,7 @@ function aggregateFragmentFromRow(row) {
14066
14731
  modelCallCount: row.model_call_count,
14067
14732
  usage: {
14068
14733
  reportedCallCount: row.reported_usage_call_count,
14734
+ completeCallCount: row.complete_usage_call_count,
14069
14735
  inputTokens: meterFragment(row.input_tokens_count, row.input_tokens_sum),
14070
14736
  outputTokens: meterFragment(row.output_tokens_count, row.output_tokens_sum),
14071
14737
  uncachedInputTokens: meterFragment(row.uncached_input_tokens_count, row.uncached_input_tokens_sum),
@@ -15048,8 +15714,134 @@ function toStorageRow(row) {
15048
15714
  };
15049
15715
  }
15050
15716
 
15717
+ // src/pg-file-upload-session-storage.ts
15718
+ import {
15719
+ DurableFileUploadSessions,
15720
+ fileUploadSessionReapAt,
15721
+ parseFileUploadSessionRow
15722
+ } from "@sixb/core/internal/file-upload-session-storage-provider";
15723
+ class PgFileUploadSessionStorage extends DurableFileUploadSessions {
15724
+ constructor(sql) {
15725
+ super(new PgFileUploadSessionBackend(sql));
15726
+ }
15727
+ }
15728
+
15729
+ class PgFileUploadSessionBackend {
15730
+ sql;
15731
+ constructor(sql) {
15732
+ this.sql = sql;
15733
+ }
15734
+ read(run) {
15735
+ return run(new PgFileUploadSessionPersistence(this.sql, false));
15736
+ }
15737
+ transaction(run) {
15738
+ return runPgTransaction(this.sql, (tx) => run(new PgFileUploadSessionPersistence(tx, true)));
15739
+ }
15740
+ }
15741
+
15742
+ class PgFileUploadSessionPersistence {
15743
+ sql;
15744
+ locking;
15745
+ constructor(sql, locking) {
15746
+ this.sql = sql;
15747
+ this.locking = locking;
15748
+ }
15749
+ async now() {
15750
+ const [row] = await this.sql`SELECT clock_timestamp() AS now`;
15751
+ if (!row)
15752
+ throw new Error("[SixbPg] Database clock returned no row.");
15753
+ return new Date(row.now);
15754
+ }
15755
+ async insert(session) {
15756
+ const rows = await this.sql`
15757
+ INSERT INTO file_upload_sessions (
15758
+ id, project_id, owner_principal_type, owner_principal_id, strategy, status, file_name,
15759
+ media_type, logical_path, expected_size_bytes, expected_digest, provider_upload,
15760
+ signed_parts, file_ref, created_at, expires_at, completed_at, aborted_at, reap_at
15761
+ ) VALUES (
15762
+ ${session.id}, ${session.projectId}, ${session.principal.type}, ${session.principal.id},
15763
+ ${session.strategy}, ${session.status}, ${session.fileName ?? null}, ${session.mediaType ?? null},
15764
+ ${session.logicalPath ?? null}, ${session.expectedSizeBytes ?? null},
15765
+ ${session.expectedDigest ?? null}, ${jsonText(session.providerUpload)}::text::jsonb,
15766
+ ${JSON.stringify(session.signedParts)}::text::jsonb, ${jsonText(session.fileRef)}::text::jsonb,
15767
+ ${session.createdAt}, ${session.expiresAt}, ${session.completedAt ?? null},
15768
+ ${session.abortedAt ?? null}, ${fileUploadSessionReapAt(session)}
15769
+ )
15770
+ ON CONFLICT (id) DO NOTHING
15771
+ RETURNING id
15772
+ `;
15773
+ return rows.length === 1;
15774
+ }
15775
+ async get(uploadId) {
15776
+ const [row] = this.locking ? await this.sql`
15777
+ SELECT * FROM file_upload_sessions WHERE id = ${uploadId} FOR UPDATE
15778
+ ` : await this.sql`
15779
+ SELECT * FROM file_upload_sessions WHERE id = ${uploadId}
15780
+ `;
15781
+ return row ? sessionFromRow(row) : null;
15782
+ }
15783
+ async update(session) {
15784
+ await this.sql`
15785
+ UPDATE file_upload_sessions
15786
+ SET status = ${session.status},
15787
+ provider_upload = ${jsonText(session.providerUpload)}::text::jsonb,
15788
+ signed_parts = ${JSON.stringify(session.signedParts)}::text::jsonb,
15789
+ file_ref = ${jsonText(session.fileRef)}::text::jsonb,
15790
+ created_at = ${session.createdAt},
15791
+ expires_at = ${session.expiresAt},
15792
+ completed_at = ${session.completedAt ?? null},
15793
+ aborted_at = ${session.abortedAt ?? null},
15794
+ reap_at = ${fileUploadSessionReapAt(session)}
15795
+ WHERE id = ${session.id}
15796
+ `;
15797
+ }
15798
+ async listAbandoned(input) {
15799
+ const rows = await this.sql`
15800
+ SELECT * FROM file_upload_sessions
15801
+ WHERE project_id = ${input.projectId} AND status = 'pending'
15802
+ AND provider_upload IS NOT NULL AND expires_at <= ${input.now}
15803
+ ORDER BY expires_at, id COLLATE "C"
15804
+ LIMIT ${input.limit}
15805
+ `;
15806
+ return rows.map(sessionFromRow);
15807
+ }
15808
+ async deleteReapable(now) {
15809
+ const rows = await this.sql`
15810
+ DELETE FROM file_upload_sessions
15811
+ WHERE reap_at IS NOT NULL AND reap_at <= ${now}
15812
+ RETURNING id
15813
+ `;
15814
+ return rows.length;
15815
+ }
15816
+ }
15817
+ function sessionFromRow(row) {
15818
+ return parseFileUploadSessionRow({
15819
+ id: row.id,
15820
+ projectId: row.project_id,
15821
+ ownerPrincipalType: row.owner_principal_type,
15822
+ ownerPrincipalId: row.owner_principal_id,
15823
+ strategy: row.strategy,
15824
+ status: row.status,
15825
+ fileName: row.file_name,
15826
+ mediaType: row.media_type,
15827
+ logicalPath: row.logical_path,
15828
+ expectedSizeBytes: row.expected_size_bytes,
15829
+ expectedDigest: row.expected_digest,
15830
+ providerUpload: row.provider_upload,
15831
+ signedParts: row.signed_parts,
15832
+ fileRef: row.file_ref,
15833
+ createdAt: row.created_at,
15834
+ expiresAt: row.expires_at,
15835
+ completedAt: row.completed_at,
15836
+ abortedAt: row.aborted_at
15837
+ });
15838
+ }
15839
+ function jsonText(value) {
15840
+ return value === undefined ? null : JSON.stringify(value);
15841
+ }
15842
+
15051
15843
  // src/pg-pipeline-run-storage.ts
15052
- import { parseSixbFailure as parseSixbFailure3, serializeSixbFailure as serializeSixbFailure3 } from "@sixb/core/internal/errors";
15844
+ import { parseSixbFailure as parseSixbFailure4, serializeSixbFailure as serializeSixbFailure3 } from "@sixb/core/internal/errors";
15053
15845
  import { assertPipelineRunExecution } from "@sixb/core/internal/pipeline-run-storage-provider";
15054
15846
  import { canRequeuePipelineRunAfterEnqueueFailure } from "@sixb/core/internal/storage";
15055
15847
  import { PIPELINE_RUN_FAILURE_CODES, PipelineRunError } from "@sixb/core/storage";
@@ -15403,7 +16195,7 @@ function rowToPipelineRunRecord(row) {
15403
16195
  datasetId: row.output_dataset_id,
15404
16196
  versionId: row.output_version_id
15405
16197
  } : undefined,
15406
- error: row.error === null ? undefined : parseSixbFailure3(row.error, PIPELINE_RUN_FAILURE_CODES)
16198
+ error: row.error === null ? undefined : parseSixbFailure4(row.error, PIPELINE_RUN_FAILURE_CODES)
15407
16199
  };
15408
16200
  }
15409
16201
  function rowToPipelineStepRunRecord(row) {
@@ -15424,7 +16216,7 @@ function rowToPipelineStepRunRecord(row) {
15424
16216
  versionId: row.output_version_id
15425
16217
  } : undefined,
15426
16218
  rowsWritten: row.rows_written != null ? Number(row.rows_written) : undefined,
15427
- error: row.error === null ? undefined : parseSixbFailure3(row.error, PIPELINE_RUN_FAILURE_CODES)
16219
+ error: row.error === null ? undefined : parseSixbFailure4(row.error, PIPELINE_RUN_FAILURE_CODES)
15428
16220
  };
15429
16221
  }
15430
16222
  function parseDatasetVersionRefs(value) {
@@ -16150,7 +16942,7 @@ function toIsoString3(value) {
16150
16942
  }
16151
16943
 
16152
16944
  // src/pg-sync-run-storage.ts
16153
- import { parseSixbFailure as parseSixbFailure4, serializeSixbFailure as serializeSixbFailure5 } from "@sixb/core/internal/errors";
16945
+ import { parseSixbFailure as parseSixbFailure5, serializeSixbFailure as serializeSixbFailure5 } from "@sixb/core/internal/errors";
16154
16946
  import { canRequeueSyncRunAfterEnqueueFailure } from "@sixb/core/internal/storage";
16155
16947
  import { assertSyncRunExecution } from "@sixb/core/internal/sync-run-storage-provider";
16156
16948
  import { SYNC_RUN_FAILURE_CODES, SyncRunError } from "@sixb/core/storage";
@@ -16376,7 +17168,7 @@ function rowToSyncRunRecord(row) {
16376
17168
  } : undefined,
16377
17169
  expectedLatestVersionId: row.expected_latest_version_id ?? undefined,
16378
17170
  commitMessage: row.commit_message ?? undefined,
16379
- error: row.error === null ? undefined : parseSixbFailure4(row.error, SYNC_RUN_FAILURE_CODES),
17171
+ error: row.error === null ? undefined : parseSixbFailure5(row.error, SYNC_RUN_FAILURE_CODES),
16380
17172
  checkpoint: hasCheckpoint(row) ? row.checkpoint : undefined
16381
17173
  };
16382
17174
  }
@@ -16501,7 +17293,7 @@ function rowToPoint(row) {
16501
17293
  }
16502
17294
 
16503
17295
  // src/pg-webhook-run-storage.ts
16504
- import { parseSixbFailure as parseSixbFailure5, serializeSixbFailure as serializeSixbFailure6 } from "@sixb/core/internal/errors";
17296
+ import { parseSixbFailure as parseSixbFailure6, serializeSixbFailure as serializeSixbFailure6 } from "@sixb/core/internal/errors";
16505
17297
  import { assertWebhookRunExecution } from "@sixb/core/internal/webhook-run-storage-provider";
16506
17298
  import { canRetryWebhookRun, WEBHOOK_RUN_FAILURE_CODES, WebhookRunError } from "@sixb/core/storage";
16507
17299
  class PgWebhookRunStorage {
@@ -16655,7 +17447,7 @@ function rowToWebhookRunRecord(row) {
16655
17447
  requestBodySha256: row.request_body_sha256,
16656
17448
  responseStatus: row.response_status != null ? Number(row.response_status) : undefined,
16657
17449
  idempotencyKey: row.idempotency_key ?? undefined,
16658
- error: row.error === null ? undefined : parseSixbFailure5(row.error, WEBHOOK_RUN_FAILURE_CODES)
17450
+ error: row.error === null ? undefined : parseSixbFailure6(row.error, WEBHOOK_RUN_FAILURE_CODES)
16659
17451
  };
16660
17452
  }
16661
17453
 
@@ -16719,7 +17511,7 @@ class PgWorkflowInterventionStorage {
16719
17511
  SET
16720
17512
  status = ${"submitted"},
16721
17513
  submitted_at = ${input.submittedAt ?? new Date},
16722
- submitted_by = ${serializeActor(input.submittedBy)}::text::jsonb,
17514
+ submitted_by = ${serializePrincipal(input.submittedBy)}::text::jsonb,
16723
17515
  response = ${serializeRecord(input.response)}::text::jsonb
16724
17516
  WHERE project_id = ${input.projectId} AND id = ${input.id}
16725
17517
  RETURNING *
@@ -16735,7 +17527,7 @@ class PgWorkflowInterventionStorage {
16735
17527
  SET
16736
17528
  status = ${"cancelled"},
16737
17529
  cancelled_at = ${input.cancelledAt ?? new Date},
16738
- cancelled_by = ${serializeActor(input.cancelledBy)}::text::jsonb
17530
+ cancelled_by = ${serializePrincipal(input.cancelledBy)}::text::jsonb
16739
17531
  WHERE project_id = ${input.projectId} AND id = ${input.id}
16740
17532
  RETURNING *
16741
17533
  `;
@@ -16862,10 +17654,10 @@ function serializeRecord(value) {
16862
17654
  function parseRecord(value) {
16863
17655
  return typeof value === "string" ? JSON.parse(value) : value;
16864
17656
  }
16865
- function serializeActor(actor) {
16866
- return actor ? JSON.stringify(actor) : null;
17657
+ function serializePrincipal(principal) {
17658
+ return principal ? JSON.stringify(principal) : null;
16867
17659
  }
16868
- function parseActor(value) {
17660
+ function parsePrincipal(value) {
16869
17661
  return typeof value === "string" ? JSON.parse(value) : value ?? undefined;
16870
17662
  }
16871
17663
  function rowToWorkflowInterventionRecord(row) {
@@ -16885,10 +17677,10 @@ function rowToWorkflowInterventionRecord(row) {
16885
17677
  requestedAt: new Date(row.requested_at),
16886
17678
  expiresAt: row.expires_at ? new Date(row.expires_at) : undefined,
16887
17679
  submittedAt: row.submitted_at ? new Date(row.submitted_at) : undefined,
16888
- submittedBy: parseActor(row.submitted_by),
17680
+ submittedBy: parsePrincipal(row.submitted_by),
16889
17681
  response: row.response ? parseRecord(row.response) : undefined,
16890
17682
  cancelledAt: row.cancelled_at ? new Date(row.cancelled_at) : undefined,
16891
- cancelledBy: parseActor(row.cancelled_by),
17683
+ cancelledBy: parsePrincipal(row.cancelled_by),
16892
17684
  expiredAt: row.expired_at ? new Date(row.expired_at) : undefined
16893
17685
  };
16894
17686
  }
@@ -16899,7 +17691,7 @@ function assertNonNegativeInteger(value, fieldName) {
16899
17691
  }
16900
17692
 
16901
17693
  // src/pg-workflow-run-storage.ts
16902
- import { parseSixbFailure as parseSixbFailure6, serializeSixbFailure as serializeSixbFailure7 } from "@sixb/core/internal/errors";
17694
+ import { parseSixbFailure as parseSixbFailure7, serializeSixbFailure as serializeSixbFailure7 } from "@sixb/core/internal/errors";
16903
17695
  import {
16904
17696
  assertWorkflowAgentNodeRunExecution,
16905
17697
  assertWorkflowRunExecution
@@ -17541,7 +18333,7 @@ function rowToWorkflowRunRecord(row) {
17541
18333
  queuedAt: row.queued_at ? new Date(row.queued_at) : undefined,
17542
18334
  startedAt: new Date(row.started_at),
17543
18335
  finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
17544
- error: row.error === null ? undefined : parseSixbFailure6(row.error, WORKFLOW_RUN_FAILURE_CODES),
18336
+ error: row.error === null ? undefined : parseSixbFailure7(row.error, WORKFLOW_RUN_FAILURE_CODES),
17545
18337
  attempt: Number(row.attempt),
17546
18338
  ...row.execution_token && row.execution_queue_lease_expires_at ? {
17547
18339
  execution: {
@@ -17566,7 +18358,7 @@ function rowToWorkflowNodeRunRecord(row) {
17566
18358
  startedAt: new Date(row.started_at),
17567
18359
  finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
17568
18360
  output: row.output ? parseRecord2(row.output) : undefined,
17569
- error: row.error === null ? undefined : parseSixbFailure6(row.error, WORKFLOW_RUN_FAILURE_CODES)
18361
+ error: row.error === null ? undefined : parseSixbFailure7(row.error, WORKFLOW_RUN_FAILURE_CODES)
17570
18362
  };
17571
18363
  }
17572
18364
  function assertNonNegativeInteger2(value, fieldName) {
@@ -17608,7 +18400,7 @@ function rowToWorkflowAgentNodeRunRecord(row) {
17608
18400
  ...row.finish_reason ? { finishReason: row.finish_reason } : {},
17609
18401
  ...row.trace ? { trace: parseJson(row.trace) } : {},
17610
18402
  ...row.diagnostics ? { diagnostics: parseJson(row.diagnostics) } : {},
17611
- ...row.error ? { error: parseSixbFailure6(row.error, AGENT_RUN_FAILURE_CODES3) } : {},
18403
+ ...row.error ? { error: parseSixbFailure7(row.error, AGENT_RUN_FAILURE_CODES3) } : {},
17612
18404
  attempt: Number(row.attempt),
17613
18405
  ...row.execution_token && row.execution_queue_lease_expires_at ? {
17614
18406
  execution: {
@@ -17956,6 +18748,7 @@ class PostgresStorage {
17956
18748
  connectorConnections;
17957
18749
  shareGrants;
17958
18750
  shareSessions;
18751
+ fileUploadSessions;
17959
18752
  migrators;
17960
18753
  sql;
17961
18754
  schemaName;
@@ -18009,6 +18802,7 @@ class PostgresStorage {
18009
18802
  this.connectorConnections = createOperationScopedFacade(stores.connectorConnections, scope);
18010
18803
  this.shareGrants = createOperationScopedFacade(stores.shareGrants, scope);
18011
18804
  this.shareSessions = createOperationScopedFacade(stores.shareSessions, scope);
18805
+ this.fileUploadSessions = createOperationScopedFacade(stores.fileUploadSessions, scope);
18012
18806
  registerPostgresStorageTestingAdapter(this, (durationMs) => stores.connectorConnections.advanceTimeForTesting(durationMs));
18013
18807
  }
18014
18808
  async ping() {
@@ -18103,7 +18897,8 @@ function createPostgresStores(sql, options) {
18103
18897
  rules: new PgRulesStorage(sql),
18104
18898
  connectorConnections: new PgConnectorConnectionStorage(sql),
18105
18899
  shareGrants,
18106
- shareSessions
18900
+ shareSessions,
18901
+ fileUploadSessions: new PgFileUploadSessionStorage(sql)
18107
18902
  };
18108
18903
  }
18109
18904
  function resolveTimeoutMillis(value, label) {
@@ -18120,4 +18915,4 @@ export {
18120
18915
  migratePostgresStorage
18121
18916
  };
18122
18917
 
18123
- //# debugId=0CAAAAC02FB5CFD864756E2164756E21
18918
+ //# debugId=F4D8409142F8F3AF64756E2164756E21