@sixb/pg 0.1.21 → 0.1.22

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 (110) hide show
  1. package/README.md +1 -0
  2. package/dist/agents/agent-storage.d.ts +8 -2
  3. package/dist/agents/agent-storage.d.ts.map +1 -1
  4. package/dist/agents/approvals.d.ts +48 -0
  5. package/dist/agents/approvals.d.ts.map +1 -0
  6. package/dist/agents/checkpoints.d.ts +1 -0
  7. package/dist/agents/checkpoints.d.ts.map +1 -1
  8. package/dist/agents/index.d.ts +1 -0
  9. package/dist/agents/index.d.ts.map +1 -1
  10. package/dist/agents/memory.d.ts +19 -0
  11. package/dist/agents/memory.d.ts.map +1 -0
  12. package/dist/agents/messages.d.ts.map +1 -1
  13. package/dist/agents/rows.d.ts +15 -1
  14. package/dist/agents/rows.d.ts.map +1 -1
  15. package/dist/agents/runs.d.ts +3 -1
  16. package/dist/agents/runs.d.ts.map +1 -1
  17. package/dist/agents/settings.d.ts +29 -0
  18. package/dist/agents/settings.d.ts.map +1 -0
  19. package/dist/agents/threads.d.ts +6 -1
  20. package/dist/agents/threads.d.ts.map +1 -1
  21. package/dist/ai-cost-groups.d.ts.map +1 -1
  22. package/dist/auth-storage/auth-storage.d.ts +3 -2
  23. package/dist/auth-storage/auth-storage.d.ts.map +1 -1
  24. package/dist/auth-storage/users.d.ts +8 -4
  25. package/dist/auth-storage/users.d.ts.map +1 -1
  26. package/dist/index.js +3720 -1594
  27. package/dist/index.js.map +37 -31
  28. package/dist/migrations.d.ts.map +1 -1
  29. package/dist/objects/conditional-reader.d.ts +17 -0
  30. package/dist/objects/conditional-reader.d.ts.map +1 -0
  31. package/dist/objects/query-compiler.d.ts +27 -1
  32. package/dist/objects/query-compiler.d.ts.map +1 -1
  33. package/dist/objects/query-indexes.d.ts.map +1 -1
  34. package/dist/objects/read-scope.d.ts +10 -1
  35. package/dist/objects/read-scope.d.ts.map +1 -1
  36. package/dist/objects/reader.d.ts +2 -0
  37. package/dist/objects/reader.d.ts.map +1 -1
  38. package/dist/objects/scoped-reader.d.ts +12 -0
  39. package/dist/objects/scoped-reader.d.ts.map +1 -0
  40. package/dist/objects/selected-reader.d.ts +1 -0
  41. package/dist/objects/selected-reader.d.ts.map +1 -1
  42. package/dist/objects/storage.d.ts +7 -1
  43. package/dist/objects/storage.d.ts.map +1 -1
  44. package/dist/ontology-storage/index.d.ts +2 -0
  45. package/dist/ontology-storage/index.d.ts.map +1 -1
  46. package/dist/ontology-storage/materialization-session.d.ts +6 -0
  47. package/dist/ontology-storage/materialization-session.d.ts.map +1 -1
  48. package/dist/ontology-storage/materialization-writer.d.ts +15 -0
  49. package/dist/ontology-storage/materialization-writer.d.ts.map +1 -1
  50. package/dist/ontology-storage/materializations.d.ts.map +1 -1
  51. package/dist/ontology-storage/unique-values.d.ts +60 -0
  52. package/dist/ontology-storage/unique-values.d.ts.map +1 -0
  53. package/dist/pg-ai-cost-storage.d.ts.map +1 -1
  54. package/dist/pg-ai-usage-storage.d.ts +8 -1
  55. package/dist/pg-ai-usage-storage.d.ts.map +1 -1
  56. package/dist/pg-artifact-storage.d.ts +8 -1
  57. package/dist/pg-artifact-storage.d.ts.map +1 -1
  58. package/dist/pg-execution-storage.d.ts +2 -1
  59. package/dist/pg-execution-storage.d.ts.map +1 -1
  60. package/dist/storage-errors.d.ts +2 -0
  61. package/dist/storage-errors.d.ts.map +1 -1
  62. package/package.json +3 -3
  63. package/src/agents/agent-storage.ts +11 -2
  64. package/src/agents/approvals.ts +400 -0
  65. package/src/agents/checkpoints.ts +8 -4
  66. package/src/agents/index.ts +1 -0
  67. package/src/agents/memory.ts +175 -0
  68. package/src/agents/messages.ts +14 -7
  69. package/src/agents/rows.ts +46 -1
  70. package/src/agents/runs.ts +154 -22
  71. package/src/agents/settings.ts +105 -0
  72. package/src/agents/threads.ts +356 -3
  73. package/src/ai-cost-groups.ts +11 -1
  74. package/src/auth-storage/auth-storage.ts +45 -13
  75. package/src/auth-storage/users.ts +105 -43
  76. package/src/migrations/069-artifact-kinds.sql +4 -0
  77. package/src/migrations/070-artifact-state.sql +16 -0
  78. package/src/migrations/071-agent-thread-participants.sql +16 -0
  79. package/src/migrations/072-forked-context-checkpoints.sql +23 -0
  80. package/src/migrations/073-unique-values.sql +25 -0
  81. package/src/migrations/074-generated-media-usage.sql +21 -0
  82. package/src/migrations/075-model-call-files.sql +11 -0
  83. package/src/migrations/076-agent-memory.sql +33 -0
  84. package/src/migrations/077-artifact-threads.sql +21 -0
  85. package/src/migrations/078-agent-approvals.sql +58 -0
  86. package/src/migrations/079-agent-approval-resume.sql +20 -0
  87. package/src/migrations/080-subagent-threads.sql +145 -0
  88. package/src/migrations/081-subagent-questions.sql +6 -0
  89. package/src/migrations.ts +34 -0
  90. package/src/objects/conditional-reader.ts +44 -0
  91. package/src/objects/prepare-queries.ts +1 -1
  92. package/src/objects/query-compiler.ts +96 -33
  93. package/src/objects/query-indexes.ts +29 -10
  94. package/src/objects/read-scope.ts +140 -25
  95. package/src/objects/reader.ts +50 -6
  96. package/src/objects/scoped-reader.ts +101 -0
  97. package/src/objects/selected-reader.ts +16 -76
  98. package/src/objects/storage.ts +24 -5
  99. package/src/objects/text-count-index.ts +1 -0
  100. package/src/ontology-storage/index.ts +3 -0
  101. package/src/ontology-storage/materialization-session.ts +8 -0
  102. package/src/ontology-storage/materialization-writer.ts +76 -1
  103. package/src/ontology-storage/materializations.ts +20 -1
  104. package/src/ontology-storage/unique-values.ts +274 -0
  105. package/src/pg-ai-cost-storage.ts +62 -12
  106. package/src/pg-ai-usage-storage.ts +90 -1
  107. package/src/pg-artifact-storage.ts +205 -9
  108. package/src/pg-execution-storage.ts +31 -0
  109. package/src/storage-errors.ts +7 -0
  110. package/src/transactions.ts +1 -1
@@ -1,15 +1,22 @@
1
1
  import {
2
+ artifactStateBytes,
2
3
  artifactStorageErrors,
4
+ checkArtifactStateWrite,
3
5
  parseArtifactFrameGrantRow,
4
6
  parseArtifactRow,
7
+ parseArtifactStateRow,
5
8
  parseArtifactVersionRow,
6
9
  } from "@sixb/core/internal/storage"
7
10
  import type {
8
11
  ArtifactFrameGrantRecord,
9
12
  ArtifactRecord,
13
+ ArtifactStateEntry,
14
+ ArtifactStateKeyInput,
15
+ ArtifactStateScope,
10
16
  ArtifactStorage,
11
17
  ArtifactVersionInput,
12
18
  ArtifactVersionRecord,
19
+ CopyArtifactInput,
13
20
  CreateArtifactInput,
14
21
  DeleteArtifactInput,
15
22
  DeleteArtifactVersionsByOriginInput,
@@ -24,6 +31,7 @@ import type {
24
31
  ListReferencingVersionsInput,
25
32
  PublishArtifactVersionInput,
26
33
  PublishedArtifactVersion,
34
+ SetArtifactStateInput,
27
35
  } from "@sixb/core/storage"
28
36
  import type { SQLClient, SqlParameter } from "./pg-client"
29
37
  import { type PgStoreClient, runPgTransaction } from "./transactions"
@@ -51,6 +59,8 @@ const FRAME_GRANT_COLUMNS = `
51
59
  targets, created_at AS "createdAt", expires_at AS "expiresAt"
52
60
  `
53
61
 
62
+ const STATE_COLUMNS = `key, value, size_bytes AS "sizeBytes", updated_at AS "updatedAt"`
63
+
54
64
  export class PgArtifactStorage implements ArtifactStorage {
55
65
  constructor(private readonly sql: PgStoreClient) {}
56
66
 
@@ -80,7 +90,7 @@ export class PgArtifactStorage implements ArtifactStorage {
80
90
  return runPgTransaction(this.sql, async (tx) => {
81
91
  // The row lock serializes concurrent publishes of one artifact.
82
92
  const [current] = await tx<PgRow[]>`
83
- SELECT kind, next_version FROM artifacts
93
+ SELECT kind, next_version, latest_version FROM artifacts
84
94
  WHERE project_id = ${input.projectId} AND id = ${input.artifactId}
85
95
  FOR UPDATE
86
96
  `
@@ -89,6 +99,20 @@ export class PgArtifactStorage implements ArtifactStorage {
89
99
  if (kind !== input.kind) {
90
100
  throw artifactStorageErrors.kindMismatch(input.artifactId, kind, input.kind)
91
101
  }
102
+ // Under the row lock: a publish from the same base after this one finds it moved.
103
+ const latestVersion = Number(current.latest_version)
104
+ if (input.baseVersion !== undefined && latestVersion !== input.baseVersion) {
105
+ const [row] = await tx<PgRow[]>`
106
+ SELECT ${tx.unsafe(VERSION_COLUMNS)} FROM artifact_versions
107
+ WHERE project_id = ${input.projectId} AND artifact_id = ${input.artifactId}
108
+ AND version = ${latestVersion}
109
+ `
110
+ const latest = parseArtifactVersionRow(row as PgRow)
111
+ throw artifactStorageErrors.moved(input.artifactId, input.baseVersion, {
112
+ version: latest.version,
113
+ author: latest.author,
114
+ })
115
+ }
92
116
  const version = await insertVersion(
93
117
  tx,
94
118
  input.projectId,
@@ -124,15 +148,15 @@ export class PgArtifactStorage implements ArtifactStorage {
124
148
  where.push(`a.owner_principal_id = $${params.length + 1}`)
125
149
  params.push(input.owner.id)
126
150
  }
127
- if (input.originThreadId !== undefined) {
151
+ if (input.threadId !== undefined) {
128
152
  where.push(
129
153
  `EXISTS (
130
- SELECT 1 FROM artifact_versions v
131
- WHERE v.project_id = a.project_id AND v.artifact_id = a.id
132
- AND v.origin_thread_id = $${params.length + 1}
154
+ SELECT 1 FROM artifact_threads t
155
+ WHERE t.project_id = a.project_id AND t.artifact_id = a.id
156
+ AND t.thread_id = $${params.length + 1}
133
157
  )`
134
158
  )
135
- params.push(input.originThreadId)
159
+ params.push(input.threadId)
136
160
  }
137
161
  if (input.kind !== undefined) {
138
162
  where.push(`a.kind = $${params.length + 1}`)
@@ -163,6 +187,58 @@ export class PgArtifactStorage implements ArtifactStorage {
163
187
  return row ? parseArtifactVersionRow(row) : null
164
188
  }
165
189
 
190
+ async listThreadIds(input: GetArtifactInput): Promise<string[]> {
191
+ const rows = await this.sql<{ thread_id: string }[]>`
192
+ SELECT thread_id FROM artifact_threads
193
+ WHERE project_id = ${input.projectId} AND artifact_id = ${input.id}
194
+ ORDER BY thread_id COLLATE "C"
195
+ `
196
+ return rows.map((row) => row.thread_id)
197
+ }
198
+
199
+ async copy(input: CopyArtifactInput): Promise<ArtifactRecord> {
200
+ return runPgTransaction(this.sql, async (tx) => {
201
+ // The share lock keeps the source from being deleted or published to while it is copied, so
202
+ // the copy holds every version it has.
203
+ const [source] = await tx<PgRow[]>`
204
+ SELECT id FROM artifacts WHERE project_id = ${input.projectId} AND id = ${input.sourceId}
205
+ FOR SHARE
206
+ `
207
+ if (!source) throw artifactStorageErrors.notFound(input.sourceId)
208
+
209
+ const [artifact] = await tx<PgRow[]>`
210
+ INSERT INTO artifacts (
211
+ project_id, id, kind, title, owner_principal_type, owner_principal_id,
212
+ latest_version, next_version, created_at, updated_at
213
+ )
214
+ SELECT project_id, ${input.id}, kind, title, ${input.owner.type}, ${input.owner.id},
215
+ latest_version, next_version, created_at, updated_at
216
+ FROM artifacts WHERE project_id = ${input.projectId} AND id = ${input.sourceId}
217
+ ON CONFLICT (project_id, id) DO NOTHING
218
+ RETURNING ${tx.unsafe(ARTIFACT_COLUMNS)}
219
+ `
220
+ if (!artifact) throw artifactStorageErrors.exists(input.id)
221
+
222
+ await tx`
223
+ INSERT INTO artifact_versions (
224
+ project_id, artifact_id, version, title, entry, files, outline, refs,
225
+ author_principal_type, author_principal_id, created_at
226
+ )
227
+ SELECT project_id, ${input.id}, version, title, entry, files, outline, refs,
228
+ author_principal_type, author_principal_id, created_at
229
+ FROM artifact_versions
230
+ WHERE project_id = ${input.projectId} AND artifact_id = ${input.sourceId}
231
+ `
232
+ await tx`
233
+ INSERT INTO artifact_threads (project_id, artifact_id, thread_id, shared_at, copied)
234
+ VALUES (
235
+ ${input.projectId}, ${input.id}, ${input.threadId}, ${input.copiedAt ?? new Date()}, TRUE
236
+ )
237
+ `
238
+ return parseArtifactRow(artifact)
239
+ })
240
+ }
241
+
166
242
  async listVersions(input: ListArtifactVersionsInput): Promise<ListArtifactVersionsResult> {
167
243
  const [counted] = await this.sql<{ total: number }[]>`
168
244
  SELECT COUNT(*)::int AS total FROM artifact_versions
@@ -241,8 +317,97 @@ export class PgArtifactStorage implements ArtifactStorage {
241
317
  return rows.length
242
318
  }
243
319
 
320
+ async getState(input: ArtifactStateKeyInput): Promise<ArtifactStateEntry | null> {
321
+ const [row] = await this.sql<PgRow[]>`
322
+ SELECT ${this.sql.unsafe(STATE_COLUMNS)} FROM artifact_state
323
+ WHERE project_id = ${input.projectId} AND artifact_id = ${input.artifactId}
324
+ AND principal_type = ${input.principal.type} AND principal_id = ${input.principal.id}
325
+ AND key = ${input.key}
326
+ `
327
+ return row ? parseArtifactStateRow(row) : null
328
+ }
329
+
330
+ async setState(input: SetArtifactStateInput): Promise<void> {
331
+ await runPgTransaction(this.sql, async (tx) => {
332
+ // The key share lock keeps the artifact from being deleted under the write, without
333
+ // waiting on a publish.
334
+ const [artifact] = await tx<PgRow[]>`
335
+ SELECT 1 FROM artifacts WHERE project_id = ${input.projectId} AND id = ${input.artifactId}
336
+ FOR KEY SHARE
337
+ `
338
+ if (!artifact) throw artifactStorageErrors.notFound(input.artifactId)
339
+ // One reader's writes to one artifact take turns, so two at once cannot both fit under the
340
+ // limits; other readers' writes do not wait.
341
+ await tx`
342
+ SELECT pg_advisory_xact_lock(hashtextextended(${JSON.stringify([
343
+ "artifact_state",
344
+ input.projectId,
345
+ input.artifactId,
346
+ input.principal.type,
347
+ input.principal.id,
348
+ ])}, 0))
349
+ `
350
+ const [others] = await tx<PgRow[]>`
351
+ SELECT count(*) AS keys, coalesce(sum(size_bytes), 0) AS bytes FROM artifact_state
352
+ WHERE project_id = ${input.projectId} AND artifact_id = ${input.artifactId}
353
+ AND principal_type = ${input.principal.type} AND principal_id = ${input.principal.id}
354
+ AND key <> ${input.key}
355
+ `
356
+ const sizeBytes = artifactStateBytes(input.value)
357
+ checkArtifactStateWrite({
358
+ key: input.key,
359
+ valueBytes: sizeBytes,
360
+ others: { keys: Number(others?.keys ?? 0), bytes: Number(others?.bytes ?? 0) },
361
+ })
362
+ await tx`
363
+ INSERT INTO artifact_state (
364
+ project_id, artifact_id, principal_type, principal_id, key, value, size_bytes,
365
+ updated_at
366
+ ) VALUES (
367
+ ${input.projectId}, ${input.artifactId}, ${input.principal.type},
368
+ ${input.principal.id}, ${input.key}, ${input.value}, ${sizeBytes}, ${input.updatedAt}
369
+ )
370
+ ON CONFLICT (project_id, artifact_id, principal_type, principal_id, key) DO UPDATE SET
371
+ value = excluded.value, size_bytes = excluded.size_bytes,
372
+ updated_at = excluded.updated_at
373
+ `
374
+ })
375
+ }
376
+
377
+ async deleteState(input: ArtifactStateKeyInput): Promise<boolean> {
378
+ const rows = await this.sql`
379
+ DELETE FROM artifact_state
380
+ WHERE project_id = ${input.projectId} AND artifact_id = ${input.artifactId}
381
+ AND principal_type = ${input.principal.type} AND principal_id = ${input.principal.id}
382
+ AND key = ${input.key}
383
+ RETURNING key
384
+ `
385
+ return rows.length > 0
386
+ }
387
+
388
+ async listState(input: ArtifactStateScope): Promise<ArtifactStateEntry[]> {
389
+ // COLLATE "C": code-unit order, as SQLite and the in-memory store compare keys.
390
+ const rows = await this.sql<PgRow[]>`
391
+ SELECT ${this.sql.unsafe(STATE_COLUMNS)} FROM artifact_state
392
+ WHERE project_id = ${input.projectId} AND artifact_id = ${input.artifactId}
393
+ AND principal_type = ${input.principal.type} AND principal_id = ${input.principal.id}
394
+ ORDER BY key COLLATE "C"
395
+ `
396
+ return rows.map(parseArtifactStateRow)
397
+ }
398
+
399
+ async clearState(input: ArtifactStateScope): Promise<number> {
400
+ const rows = await this.sql`
401
+ DELETE FROM artifact_state
402
+ WHERE project_id = ${input.projectId} AND artifact_id = ${input.artifactId}
403
+ AND principal_type = ${input.principal.type} AND principal_id = ${input.principal.id}
404
+ RETURNING key
405
+ `
406
+ return rows.length
407
+ }
408
+
244
409
  async delete(input: DeleteArtifactInput): Promise<boolean> {
245
- // Versions and frame grants go with the artifact through ON DELETE CASCADE.
410
+ // Versions, frame grants, and state go with the artifact through ON DELETE CASCADE.
246
411
  const rows = await this.sql`
247
412
  DELETE FROM artifacts WHERE project_id = ${input.projectId} AND id = ${input.id}
248
413
  RETURNING id
@@ -269,11 +434,24 @@ export class PgArtifactStorage implements ArtifactStorage {
269
434
  `
270
435
  const affected = locked.map((row) => row.id)
271
436
  if (affected.length === 0) return 0
272
- const deleted = await tx`
437
+ const deleted = await tx<{ origin_thread_id: string }[]>`
273
438
  DELETE FROM artifact_versions
274
439
  WHERE project_id = ${input.projectId} AND origin_run_id = ${input.runId}
275
440
  AND artifact_id IN ${tx(affected)}
276
- RETURNING version
441
+ RETURNING origin_thread_id
442
+ `
443
+ // A run publishes from one thread: its artifacts stay shared there only while a version
444
+ // from it remains, or when they were copied for it.
445
+ const threadId = deleted[0]?.origin_thread_id
446
+ await tx`
447
+ DELETE FROM artifact_threads t
448
+ WHERE t.project_id = ${input.projectId} AND t.artifact_id IN ${tx(affected)}
449
+ AND t.thread_id = ${threadId ?? null} AND NOT t.copied
450
+ AND NOT EXISTS (
451
+ SELECT 1 FROM artifact_versions v
452
+ WHERE v.project_id = t.project_id AND v.artifact_id = t.artifact_id
453
+ AND v.origin_thread_id = t.thread_id
454
+ )
277
455
  `
278
456
  // An artifact left without versions goes; the rest point at their newest remaining one.
279
457
  await tx`
@@ -321,5 +499,23 @@ async function insertVersion(
321
499
  )
322
500
  RETURNING ${sql.unsafe(VERSION_COLUMNS)}
323
501
  `
502
+ if (input.origin) {
503
+ await linkThread(sql, projectId, artifactId, input.origin.threadId, input.createdAt)
504
+ }
324
505
  return parseArtifactVersionRow(row)
325
506
  }
507
+
508
+ /** Shares an artifact in a thread, keeping when it was first shared there. */
509
+ async function linkThread(
510
+ sql: SQLClient,
511
+ projectId: string,
512
+ artifactId: string,
513
+ threadId: string,
514
+ sharedAt: Date
515
+ ): Promise<void> {
516
+ await sql`
517
+ INSERT INTO artifact_threads (project_id, artifact_id, thread_id, shared_at)
518
+ VALUES (${projectId}, ${artifactId}, ${threadId}, ${sharedAt})
519
+ ON CONFLICT (project_id, artifact_id, thread_id) DO NOTHING
520
+ `
521
+ }
@@ -7,8 +7,10 @@ import {
7
7
  } from "@sixb/core/internal/execution-storage"
8
8
  import type {
9
9
  CreateExecutionInput,
10
+ ExecutionDescendant,
10
11
  ExecutionRecord,
11
12
  ExecutionStorage,
13
+ ListExecutionDescendantsInput,
12
14
  ListSessionExecutionsInput,
13
15
  SessionExecutions,
14
16
  ShareSessionStorage,
@@ -163,6 +165,35 @@ export class PgExecutionStorage implements ExecutionStorage {
163
165
  truncated: rows.length > input.limit,
164
166
  }
165
167
  }
168
+
169
+ async listDescendants(
170
+ input: ListExecutionDescendantsInput
171
+ ): Promise<readonly ExecutionDescendant[]> {
172
+ if (input.executionIds.length === 0) return []
173
+ const ids = this.sql.array([...new Set(input.executionIds)])
174
+
175
+ // UNION, not UNION ALL: an execution asked about may sit below another one asked about, and
176
+ // the walk would otherwise reach what it caused twice.
177
+ const rows = await this.sql<{ id: string; parent_id: string }[]>`
178
+ WITH RECURSIVE tree(id, parent_id) AS (
179
+ SELECT child.id, child.parent_execution_id
180
+ FROM executions child
181
+ WHERE child.project_id = ${input.projectId}
182
+ AND child.parent_execution_id = ANY(${ids}::text[])
183
+ UNION
184
+ SELECT child.id, child.parent_execution_id
185
+ FROM tree
186
+ JOIN executions child
187
+ ON child.project_id = ${input.projectId}
188
+ AND child.parent_execution_id = tree.id
189
+ )
190
+ SELECT id, parent_id
191
+ FROM tree
192
+ WHERE id <> ALL(${ids}::text[])
193
+ ORDER BY id COLLATE "C"
194
+ `
195
+ return rows.map((row) => ({ id: row.id, parentId: row.parent_id }))
196
+ }
166
197
  }
167
198
 
168
199
  /** An `executions` row as its record, for any store that reads executions alongside its own rows. */
@@ -81,6 +81,13 @@ export function isUniqueViolation(error: unknown): boolean {
81
81
  return pgErrorCode(error) === UNIQUE_VIOLATION
82
82
  }
83
83
 
84
+ /** True when the error is a unique violation of exactly the named constraint. */
85
+ export function isUniqueViolationOf(error: unknown, constraint: string): boolean {
86
+ if (!isUniqueViolation(error)) return false
87
+
88
+ return (error as { readonly constraint_name?: unknown }).constraint_name === constraint
89
+ }
90
+
84
91
  /** True when the error is a PostgreSQL foreign-key violation (SQLSTATE 23503). */
85
92
  export function isForeignKeyViolation(error: unknown): boolean {
86
93
  return pgErrorCode(error) === FOREIGN_KEY_VIOLATION
@@ -159,7 +159,7 @@ export async function runPgRepeatableReadTransaction<T>(
159
159
  }
160
160
 
161
161
  throw new Error(
162
- '[SixbPg] Selected object reads cannot join an unverified PostgreSQL transaction. Use storage.transaction(..., { isolation: "serializable" }) when reading through tx.objects.'
162
+ '[SixbPg] Scoped object reads cannot join an unverified PostgreSQL transaction. Use storage.transaction(..., { isolation: "serializable" }) when reading through tx.objects.'
163
163
  )
164
164
  }
165
165