@sixb/pg 0.1.1 → 0.1.3

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 (112) hide show
  1. package/README.md +7 -6
  2. package/dist/agents/agent-storage.d.ts +7 -4
  3. package/dist/agents/agent-storage.d.ts.map +1 -1
  4. package/dist/agents/checkpoints.d.ts +12 -0
  5. package/dist/agents/checkpoints.d.ts.map +1 -0
  6. package/dist/agents/index.d.ts +1 -0
  7. package/dist/agents/index.d.ts.map +1 -1
  8. package/dist/agents/messages.d.ts +2 -1
  9. package/dist/agents/messages.d.ts.map +1 -1
  10. package/dist/agents/rows.d.ts +22 -12
  11. package/dist/agents/rows.d.ts.map +1 -1
  12. package/dist/agents/runs.d.ts +3 -2
  13. package/dist/agents/runs.d.ts.map +1 -1
  14. package/dist/auth-storage/service-accounts.d.ts.map +1 -1
  15. package/dist/auth-storage/shared.d.ts +0 -4
  16. package/dist/auth-storage/shared.d.ts.map +1 -1
  17. package/dist/connector-connection-storage.d.ts +9 -0
  18. package/dist/connector-connection-storage.d.ts.map +1 -0
  19. package/dist/index.d.ts +9 -5
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +5010 -1837
  22. package/dist/index.js.map +31 -26
  23. package/dist/latest-run-query.d.ts.map +1 -1
  24. package/dist/migrations.d.ts.map +1 -1
  25. package/dist/ontology-storage/materialization-session.d.ts.map +1 -1
  26. package/dist/ontology-storage/materialization-state.d.ts +0 -2
  27. package/dist/ontology-storage/materialization-state.d.ts.map +1 -1
  28. package/dist/ontology-storage/materialization-writer.d.ts.map +1 -1
  29. package/dist/ontology-storage/materializations.d.ts.map +1 -1
  30. package/dist/ontology-storage/outbox.d.ts.map +1 -1
  31. package/dist/ontology-storage/shared.d.ts +3 -6
  32. package/dist/ontology-storage/shared.d.ts.map +1 -1
  33. package/dist/pg-action-run-storage.d.ts +3 -2
  34. package/dist/pg-action-run-storage.d.ts.map +1 -1
  35. package/dist/pg-ai-cost-storage.d.ts +12 -0
  36. package/dist/pg-ai-cost-storage.d.ts.map +1 -0
  37. package/dist/pg-ai-usage-storage.d.ts +16 -0
  38. package/dist/pg-ai-usage-storage.d.ts.map +1 -0
  39. package/dist/pg-execution-storage.d.ts +14 -0
  40. package/dist/pg-execution-storage.d.ts.map +1 -0
  41. package/dist/pg-pipeline-run-storage.d.ts +5 -2
  42. package/dist/pg-pipeline-run-storage.d.ts.map +1 -1
  43. package/dist/pg-projection-run-storage.d.ts +5 -2
  44. package/dist/pg-projection-run-storage.d.ts.map +1 -1
  45. package/dist/pg-sync-run-storage.d.ts +5 -2
  46. package/dist/pg-sync-run-storage.d.ts.map +1 -1
  47. package/dist/pg-webhook-run-storage.d.ts +5 -2
  48. package/dist/pg-webhook-run-storage.d.ts.map +1 -1
  49. package/dist/pg-workflow-run-storage.d.ts +5 -3
  50. package/dist/pg-workflow-run-storage.d.ts.map +1 -1
  51. package/dist/run-list-query.d.ts +1 -1
  52. package/dist/run-list-query.d.ts.map +1 -1
  53. package/dist/testing.d.ts +6 -0
  54. package/dist/testing.d.ts.map +1 -0
  55. package/package.json +3 -3
  56. package/src/agents/agent-storage.ts +9 -5
  57. package/src/agents/checkpoints.ts +175 -0
  58. package/src/agents/index.ts +1 -0
  59. package/src/agents/messages.ts +71 -13
  60. package/src/agents/rows.ts +57 -43
  61. package/src/agents/runs.ts +29 -15
  62. package/src/auth-storage/service-accounts.ts +14 -15
  63. package/src/auth-storage/shared.ts +0 -8
  64. package/src/connector-connection-storage.ts +760 -0
  65. package/src/index.ts +38 -15
  66. package/src/latest-run-query.ts +7 -1
  67. package/src/migrations/004-executions.sql +140 -0
  68. package/src/migrations/005-workflow-executions.sql +10 -0
  69. package/src/migrations/006-narrow-ontology-source-root-index.sql +4 -0
  70. package/src/migrations/007-split-overrides.sql +153 -0
  71. package/src/migrations/008-action-executions.sql +7 -0
  72. package/src/migrations/009-agent-executions.sql +21 -0
  73. package/src/migrations/010-ai-usage-accounting-foundation.sql +76 -0
  74. package/src/migrations/011-sync-failure-record.sql +23 -0
  75. package/src/migrations/012-pipeline-failure-record.sql +47 -0
  76. package/src/migrations/013-workflow-failure-record.sql +45 -0
  77. package/src/migrations/014-agent-failure-record.sql +56 -0
  78. package/src/migrations/015-projection-failure-record.sql +20 -0
  79. package/src/migrations/016-webhook-run-failure-record.sql +19 -0
  80. package/src/migrations/017-action-failure-record.sql +65 -0
  81. package/src/migrations/018-ontology-outbox-failure-record.sql +29 -0
  82. package/src/migrations/019-webhook-delivery-failure-record.sql +27 -0
  83. package/src/migrations/020-drop-run-usage-projections.sql +8 -0
  84. package/src/migrations/021-sync-pipeline-executions.sql +50 -0
  85. package/src/migrations/022-projection-executions.sql +115 -0
  86. package/src/migrations/023-webhook-executions.sql +30 -0
  87. package/src/migrations/024-ontology-commit-executions.sql +12 -0
  88. package/src/migrations/025-connector-connections.sql +231 -0
  89. package/src/migrations/026-ai-cost-accounting.sql +53 -0
  90. package/src/migrations/027-agent-context-checkpoints.sql +34 -0
  91. package/src/migrations/028-object-override-edit-times.sql +3 -0
  92. package/src/migrations.ts +82 -0
  93. package/src/ontology-storage/materialization-session.ts +25 -28
  94. package/src/ontology-storage/materialization-state.ts +161 -43
  95. package/src/ontology-storage/materialization-writer.ts +149 -76
  96. package/src/ontology-storage/materializations.ts +3 -1
  97. package/src/ontology-storage/outbox.ts +9 -2
  98. package/src/ontology-storage/shared.ts +10 -20
  99. package/src/pg-action-run-storage.ts +43 -67
  100. package/src/pg-ai-cost-storage.ts +707 -0
  101. package/src/pg-ai-usage-storage.ts +290 -0
  102. package/src/pg-execution-storage.ts +147 -0
  103. package/src/pg-pipeline-run-storage.ts +107 -41
  104. package/src/pg-projection-run-storage.ts +159 -91
  105. package/src/pg-sync-run-storage.ts +100 -28
  106. package/src/pg-webhook-run-storage.ts +89 -57
  107. package/src/pg-workflow-run-storage.ts +97 -130
  108. package/src/run-list-query.ts +9 -4
  109. package/src/testing.ts +18 -0
  110. package/dist/pg-webhook-delivery-storage.d.ts +0 -17
  111. package/dist/pg-webhook-delivery-storage.d.ts.map +0 -1
  112. package/src/pg-webhook-delivery-storage.ts +0 -180
package/src/index.ts CHANGED
@@ -19,6 +19,8 @@ import {
19
19
  createWorkflowRunOperationScope,
20
20
  } from "@sixb/core/internal/storage-operation-scope"
21
21
  import {
22
+ type AiCostStorage,
23
+ type AiUsageStorage,
22
24
  createTransactionStorageProxy,
23
25
  type ObjectStorage,
24
26
  StorageTransactionError,
@@ -26,21 +28,25 @@ import {
26
28
  } from "@sixb/core/storage"
27
29
  import { PgAgentStorage } from "./agents"
28
30
  import { PgAuthStorage } from "./auth-storage"
31
+ import { PgConnectorConnectionStorage } from "./connector-connection-storage"
29
32
  import { createPostgresStorageMigrators, dropSchema } from "./migrations"
30
33
  import { PgOntologyStorage, type PgOntologyTransactionContext } from "./ontology-storage"
31
34
  import { PgActionRunStorage } from "./pg-action-run-storage"
35
+ import { PgAiCostStorage } from "./pg-ai-cost-storage"
36
+ import { PgAiUsageStorage } from "./pg-ai-usage-storage"
32
37
  import { createPgClient, type SQL, type SQLClient } from "./pg-client"
38
+ import { PgExecutionStorage } from "./pg-execution-storage"
33
39
  import { PgObjectStorage } from "./pg-object-storage"
34
40
  import { PgPipelineRunStorage } from "./pg-pipeline-run-storage"
35
41
  import { PgProjectionRunStorage } from "./pg-projection-run-storage"
36
42
  import { PgRulesStorage } from "./pg-rules-storage"
37
43
  import { PgSyncRunStorage } from "./pg-sync-run-storage"
38
44
  import { PgTimeseriesStorage } from "./pg-timeseries-storage"
39
- import { PgWebhookDeliveryStorage } from "./pg-webhook-delivery-storage"
40
45
  import { PgWebhookRunStorage } from "./pg-webhook-run-storage"
41
46
  import { PgWorkflowInterventionStorage } from "./pg-workflow-intervention-storage"
42
47
  import { PgWorkflowRunStorage } from "./pg-workflow-run-storage"
43
48
  import { isRetryableTransactionConflict } from "./storage-errors"
49
+ import { registerPostgresStorageTestingAdapter } from "./testing"
44
50
  import { type PgStoreClient, runPgTransaction } from "./transactions"
45
51
 
46
52
  export interface PostgresStorageOptions {
@@ -106,8 +112,8 @@ export interface PostgresStorageOptions {
106
112
  /**
107
113
  * PostgreSQL storage provider for Sixb.
108
114
  *
109
- * Bundles Sixb storage adapters backed by a shared PostgreSQL connection pool (porsager
110
- * `postgres`), which reliably reclaims connections under load.
115
+ * Bundles Sixb storage adapters, including the AI accounting ledger, behind a shared PostgreSQL
116
+ * connection pool (porsager `postgres`), which reliably reclaims connections under load.
111
117
  *
112
118
  * The storage exposes a core `StorageMigrator`. Sixb CLI startup and
113
119
  * `sixb db migrate` run it automatically through `migrateStorage(storage)`.
@@ -128,7 +134,10 @@ export class PostgresStorage implements MigrationCapableStorage {
128
134
  readonly objects: ObjectStorage
129
135
  readonly ontology: PgOntologyStorage
130
136
  readonly auth: PgAuthStorage
137
+ readonly executions: PgExecutionStorage
131
138
  readonly agents: PgAgentStorage
139
+ readonly aiUsage: AiUsageStorage
140
+ readonly aiCosts: AiCostStorage
132
141
  readonly actionRuns: PgActionRunStorage
133
142
  readonly pipelineRuns: PgPipelineRunStorage
134
143
  readonly workflowRuns: PgWorkflowRunStorage
@@ -136,9 +145,9 @@ export class PostgresStorage implements MigrationCapableStorage {
136
145
  readonly syncRuns: PgSyncRunStorage
137
146
  readonly projectionRuns: PgProjectionRunStorage
138
147
  readonly timeseries: Storage["timeseries"]
139
- readonly webhookDeliveries: PgWebhookDeliveryStorage
140
148
  readonly webhookRuns: PgWebhookRunStorage
141
149
  readonly rules: PgRulesStorage
150
+ readonly connectorConnections: PgConnectorConnectionStorage
142
151
  readonly migrators: readonly StorageMigrator[]
143
152
 
144
153
  private readonly sql: SQL
@@ -199,7 +208,10 @@ export class PostgresStorage implements MigrationCapableStorage {
199
208
  this.objects = createOperationScopedFacade(stores.objects, scope)
200
209
  this.ontology = createOntologyOperationScope(stores.ontology, scope)
201
210
  this.auth = createAuthOperationScope(stores.auth, scope)
211
+ this.executions = createOperationScopedFacade(stores.executions, scope)
202
212
  this.agents = createAgentOperationScope(stores.agents, scope)
213
+ this.aiUsage = createOperationScopedFacade(stores.aiUsage, scope)
214
+ this.aiCosts = createOperationScopedFacade(stores.aiCosts, scope)
203
215
  this.actionRuns = createOperationScopedFacade(stores.actionRuns, scope)
204
216
  this.pipelineRuns = createOperationScopedFacade(stores.pipelineRuns, scope)
205
217
  this.workflowRuns = createWorkflowRunOperationScope(stores.workflowRuns, scope)
@@ -207,9 +219,12 @@ export class PostgresStorage implements MigrationCapableStorage {
207
219
  this.syncRuns = createOperationScopedFacade(stores.syncRuns, scope)
208
220
  this.projectionRuns = createOperationScopedFacade(stores.projectionRuns, scope)
209
221
  this.timeseries = createOperationScopedFacade(stores.timeseries, scope)
210
- this.webhookDeliveries = createOperationScopedFacade(stores.webhookDeliveries, scope)
211
222
  this.webhookRuns = createOperationScopedFacade(stores.webhookRuns, scope)
212
223
  this.rules = createOperationScopedFacade(stores.rules, scope)
224
+ this.connectorConnections = createOperationScopedFacade(stores.connectorConnections, scope)
225
+ registerPostgresStorageTestingAdapter(this, (durationMs) =>
226
+ stores.connectorConnections.advanceTimeForTesting(durationMs)
227
+ )
213
228
  }
214
229
 
215
230
  async ping(): Promise<void> {
@@ -313,6 +328,8 @@ function createPostgresStores(
313
328
  readonly transactionContext: PgOntologyTransactionContext | null
314
329
  }
315
330
  ): PostgresStoreSet {
331
+ const auth = new PgAuthStorage({ sql })
332
+ const executions = new PgExecutionStorage(sql, auth)
316
333
  return {
317
334
  objects: new PgObjectStorage(sql),
318
335
  ontology: new PgOntologyStorage({
@@ -320,18 +337,21 @@ function createPostgresStores(
320
337
  runRootOperation: options.runOntologyOperation,
321
338
  transactionContext: options.transactionContext,
322
339
  }),
323
- auth: new PgAuthStorage({ sql }),
324
- agents: new PgAgentStorage({ sql }),
325
- actionRuns: new PgActionRunStorage(sql),
326
- pipelineRuns: new PgPipelineRunStorage(sql),
327
- workflowRuns: new PgWorkflowRunStorage(sql),
340
+ auth,
341
+ executions,
342
+ agents: new PgAgentStorage({ sql, executions }),
343
+ aiUsage: new PgAiUsageStorage(sql),
344
+ aiCosts: new PgAiCostStorage(sql),
345
+ actionRuns: new PgActionRunStorage(sql, executions),
346
+ pipelineRuns: new PgPipelineRunStorage(sql, executions),
347
+ workflowRuns: new PgWorkflowRunStorage(sql, executions),
328
348
  workflowInterventions: new PgWorkflowInterventionStorage(sql),
329
- syncRuns: new PgSyncRunStorage(sql),
330
- projectionRuns: new PgProjectionRunStorage(sql),
349
+ syncRuns: new PgSyncRunStorage(sql, executions),
350
+ projectionRuns: new PgProjectionRunStorage(sql, executions),
331
351
  timeseries: new PgTimeseriesStorage(sql),
332
- webhookDeliveries: new PgWebhookDeliveryStorage(sql),
333
- webhookRuns: new PgWebhookRunStorage(sql),
352
+ webhookRuns: new PgWebhookRunStorage(sql, executions),
334
353
  rules: new PgRulesStorage(sql),
354
+ connectorConnections: new PgConnectorConnectionStorage(sql),
335
355
  }
336
356
  }
337
357
 
@@ -339,7 +359,10 @@ interface PostgresStoreSet {
339
359
  readonly objects: PgObjectStorage
340
360
  readonly ontology: PgOntologyStorage
341
361
  readonly auth: PgAuthStorage
362
+ readonly executions: PgExecutionStorage
342
363
  readonly agents: PgAgentStorage
364
+ readonly aiUsage: PgAiUsageStorage
365
+ readonly aiCosts: PgAiCostStorage
343
366
  readonly actionRuns: PgActionRunStorage
344
367
  readonly pipelineRuns: PgPipelineRunStorage
345
368
  readonly workflowRuns: PgWorkflowRunStorage
@@ -347,9 +370,9 @@ interface PostgresStoreSet {
347
370
  readonly syncRuns: PgSyncRunStorage
348
371
  readonly projectionRuns: PgProjectionRunStorage
349
372
  readonly timeseries: PgTimeseriesStorage
350
- readonly webhookDeliveries: PgWebhookDeliveryStorage
351
373
  readonly webhookRuns: PgWebhookRunStorage
352
374
  readonly rules: PgRulesStorage
375
+ readonly connectorConnections: PgConnectorConnectionStorage
353
376
  }
354
377
 
355
378
  function resolveTimeoutMillis(value: number | undefined, label: string): number | undefined {
@@ -23,12 +23,18 @@ export async function queryLatestRunsByOwnerId<TRow>(
23
23
  }
24
24
 
25
25
  const placeholders = ownerIds.map((_, index) => `$${index + 2}`).join(", ")
26
+ const orderColumn =
27
+ input.tableName === "sync_runs" ||
28
+ input.tableName === "pipeline_runs" ||
29
+ input.tableName === "projection_runs"
30
+ ? "COALESCE(started_at, queued_at)"
31
+ : "started_at"
26
32
  const rows = await input.sql.unsafe<TRow[]>(
27
33
  `
28
34
  SELECT DISTINCT ON (${input.ownerColumn}) ${input.selectList ?? "*"}
29
35
  FROM ${input.tableName}
30
36
  WHERE project_id = $1 AND ${input.ownerColumn} IN (${placeholders})
31
- ORDER BY ${input.ownerColumn}, started_at DESC, id DESC
37
+ ORDER BY ${input.ownerColumn}, ${orderColumn} DESC, id DESC
32
38
  `,
33
39
  [input.projectId, ...ownerIds] as SqlParameter[]
34
40
  )
@@ -0,0 +1,140 @@
1
+ CREATE TABLE executions (
2
+ project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
3
+ id TEXT NOT NULL CHECK (length(trim(id)) > 0),
4
+
5
+ executor_kind TEXT NOT NULL CHECK (
6
+ executor_kind IN (
7
+ 'request', 'action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow',
8
+ 'agent', 'kernel'
9
+ )
10
+ ),
11
+ executor_id TEXT NOT NULL CHECK (length(trim(executor_id)) > 0),
12
+ source_kind TEXT NOT NULL CHECK (
13
+ source_kind IN ('http', 'webhook', 'schedule', 'event', 'execution')
14
+ ),
15
+ source_id TEXT NOT NULL CHECK (length(trim(source_id)) > 0),
16
+
17
+ requested_by_user_id TEXT,
18
+ requested_by_service_account_id TEXT,
19
+ correlation_id TEXT NOT NULL CHECK (length(trim(correlation_id)) > 0),
20
+ parent_execution_id TEXT,
21
+
22
+ authority_kind TEXT NOT NULL CHECK (
23
+ authority_kind IN ('principal', 'trustedPrimitive', 'kernel', 'disabled')
24
+ ),
25
+ authority_user_id TEXT,
26
+ authority_service_account_id TEXT,
27
+ authority_session_id TEXT,
28
+ authority_access_token_id TEXT,
29
+ authority_primitive_kind TEXT CHECK (
30
+ authority_primitive_kind IS NULL
31
+ OR authority_primitive_kind IN (
32
+ 'action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow'
33
+ )
34
+ ),
35
+ authority_primitive_id TEXT,
36
+ authority_kernel_operation TEXT CHECK (
37
+ authority_kernel_operation IS NULL OR authority_kernel_operation = 'ontology.recover'
38
+ ),
39
+
40
+ created_at TIMESTAMPTZ NOT NULL,
41
+
42
+ PRIMARY KEY (project_id, id),
43
+ FOREIGN KEY (project_id, parent_execution_id)
44
+ REFERENCES executions (project_id, id),
45
+ FOREIGN KEY (project_id, requested_by_user_id)
46
+ REFERENCES auth_users (project_id, id),
47
+ FOREIGN KEY (project_id, requested_by_service_account_id)
48
+ REFERENCES auth_service_accounts (project_id, id),
49
+ FOREIGN KEY (project_id, authority_user_id)
50
+ REFERENCES auth_users (project_id, id),
51
+ FOREIGN KEY (project_id, authority_service_account_id)
52
+ REFERENCES auth_service_accounts (project_id, id),
53
+ FOREIGN KEY (project_id, authority_session_id)
54
+ REFERENCES auth_sessions (project_id, id),
55
+ FOREIGN KEY (project_id, authority_access_token_id)
56
+ REFERENCES auth_access_tokens (project_id, id),
57
+
58
+ CHECK (num_nonnulls(requested_by_user_id, requested_by_service_account_id) <= 1),
59
+ CHECK (
60
+ (source_kind = 'execution' AND parent_execution_id = source_id)
61
+ OR (source_kind <> 'execution' AND parent_execution_id IS NULL)
62
+ ),
63
+ CHECK (
64
+ (
65
+ authority_kind = 'principal'
66
+ AND num_nonnulls(authority_user_id, authority_service_account_id) = 1
67
+ AND num_nonnulls(authority_session_id, authority_access_token_id) <= 1
68
+ AND (authority_session_id IS NULL OR authority_user_id IS NOT NULL)
69
+ AND authority_primitive_kind IS NULL
70
+ AND authority_primitive_id IS NULL
71
+ AND authority_kernel_operation IS NULL
72
+ )
73
+ OR (
74
+ authority_kind = 'trustedPrimitive'
75
+ AND authority_user_id IS NULL
76
+ AND authority_service_account_id IS NULL
77
+ AND authority_session_id IS NULL
78
+ AND authority_access_token_id IS NULL
79
+ AND authority_primitive_kind IS NOT NULL
80
+ AND authority_primitive_id IS NOT NULL
81
+ AND length(trim(authority_primitive_id)) > 0
82
+ AND authority_kernel_operation IS NULL
83
+ )
84
+ OR (
85
+ authority_kind = 'kernel'
86
+ AND authority_user_id IS NULL
87
+ AND authority_service_account_id IS NULL
88
+ AND authority_session_id IS NULL
89
+ AND authority_access_token_id IS NULL
90
+ AND authority_primitive_kind IS NULL
91
+ AND authority_primitive_id IS NULL
92
+ AND authority_kernel_operation IS NOT NULL
93
+ )
94
+ OR (
95
+ authority_kind = 'disabled'
96
+ AND authority_user_id IS NULL
97
+ AND authority_service_account_id IS NULL
98
+ AND authority_session_id IS NULL
99
+ AND authority_access_token_id IS NULL
100
+ AND authority_primitive_kind IS NULL
101
+ AND authority_primitive_id IS NULL
102
+ AND authority_kernel_operation IS NULL
103
+ )
104
+ ),
105
+ CHECK (
106
+ (
107
+ executor_kind = 'request'
108
+ AND source_kind = 'http'
109
+ AND executor_id = source_id
110
+ AND (
111
+ (
112
+ authority_kind = 'principal'
113
+ AND requested_by_user_id IS NOT DISTINCT FROM authority_user_id
114
+ AND requested_by_service_account_id IS NOT DISTINCT FROM authority_service_account_id
115
+ )
116
+ OR (
117
+ authority_kind = 'disabled'
118
+ AND requested_by_user_id IS NULL
119
+ AND requested_by_service_account_id IS NULL
120
+ )
121
+ )
122
+ )
123
+ OR (
124
+ executor_kind IN ('action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow')
125
+ AND authority_kind = 'trustedPrimitive'
126
+ AND executor_kind = authority_primitive_kind
127
+ )
128
+ OR (
129
+ executor_kind = 'agent'
130
+ AND authority_kind = 'principal'
131
+ AND authority_service_account_id IS NOT NULL
132
+ AND authority_session_id IS NULL
133
+ AND authority_access_token_id IS NULL
134
+ )
135
+ OR (
136
+ executor_kind = 'kernel'
137
+ AND authority_kind = 'kernel'
138
+ )
139
+ )
140
+ );
@@ -0,0 +1,10 @@
1
+ ALTER TABLE workflow_runs
2
+ ADD COLUMN execution_id TEXT NOT NULL,
3
+ DROP COLUMN source,
4
+ DROP COLUMN requested_by_principal_type,
5
+ DROP COLUMN requested_by_principal_id,
6
+ ADD CONSTRAINT fk_workflow_runs_execution
7
+ FOREIGN KEY (project_id, execution_id)
8
+ REFERENCES executions (project_id, id)
9
+ ON DELETE RESTRICT,
10
+ ADD CONSTRAINT uq_workflow_runs_execution UNIQUE (project_id, execution_id);
@@ -0,0 +1,4 @@
1
+ DROP INDEX idx_ontology_source_rows_root;
2
+
3
+ CREATE INDEX idx_ontology_source_rows_root
4
+ ON ontology_source_rows (project_id, source_id, materialization_id, root_sort_key);
@@ -0,0 +1,153 @@
1
+ CREATE TABLE ontology_object_overrides (
2
+ project_id TEXT NOT NULL,
3
+ object_type_id TEXT NOT NULL,
4
+ primary_id TEXT NOT NULL,
5
+ value JSONB NOT NULL CHECK (jsonb_typeof(value) = 'object'),
6
+ last_commit_id TEXT NOT NULL,
7
+ updated_at TIMESTAMPTZ NOT NULL,
8
+ PRIMARY KEY (project_id, object_type_id, primary_id),
9
+ CHECK (COALESCE(value->>'kind' IN ('create', 'patch', 'delete'), false))
10
+ );
11
+
12
+ CREATE TABLE ontology_link_overrides (
13
+ project_id TEXT NOT NULL,
14
+ identity_kind TEXT NOT NULL CHECK (identity_kind IN ('edge', 'slot')),
15
+ identity_key JSONB NOT NULL,
16
+ source_type_id TEXT NOT NULL,
17
+ source_primary_id TEXT NOT NULL,
18
+ link_id TEXT NOT NULL,
19
+ target_type_id TEXT NOT NULL,
20
+ target_primary_id TEXT NOT NULL,
21
+ value JSONB NOT NULL CHECK (jsonb_typeof(value) = 'object'),
22
+ last_commit_id TEXT NOT NULL,
23
+ updated_at TIMESTAMPTZ NOT NULL,
24
+ PRIMARY KEY (project_id, identity_kind, identity_key),
25
+ CHECK (
26
+ identity_key = CASE identity_kind
27
+ WHEN 'edge' THEN jsonb_build_array(
28
+ source_type_id, source_primary_id, link_id, target_type_id, target_primary_id
29
+ )
30
+ WHEN 'slot' THEN jsonb_build_array(source_type_id, source_primary_id, link_id)
31
+ END
32
+ ),
33
+ CHECK (
34
+ (identity_kind = 'edge' AND COALESCE(value->>'kind' IN ('upsert', 'delete'), false))
35
+ OR
36
+ (identity_kind = 'slot'
37
+ AND COALESCE(value->>'kind' IN ('set', 'clear', 'legacy-conflict'), false)
38
+ AND (
39
+ value->>'kind' = 'legacy-conflict'
40
+ OR (
41
+ target_type_id = value->'target'->>'objectTypeId'
42
+ AND target_primary_id = value->'target'->>'primaryId'
43
+ )
44
+ ))
45
+ )
46
+ );
47
+
48
+ CREATE INDEX idx_ontology_link_overrides_source
49
+ ON ontology_link_overrides (
50
+ project_id, identity_kind, source_type_id, source_primary_id, link_id,
51
+ target_type_id, target_primary_id
52
+ );
53
+
54
+ CREATE INDEX idx_ontology_link_overrides_target
55
+ ON ontology_link_overrides (project_id, target_type_id, target_primary_id);
56
+
57
+ INSERT INTO ontology_object_overrides (
58
+ project_id, object_type_id, primary_id, value, last_commit_id, updated_at
59
+ )
60
+ SELECT project_id, object_type_id, primary_id, value, last_commit_id, updated_at
61
+ FROM ontology_overrides
62
+ WHERE entity_kind = 'object';
63
+
64
+ INSERT INTO ontology_link_overrides (
65
+ project_id, identity_kind, identity_key,
66
+ source_type_id, source_primary_id, link_id, target_type_id, target_primary_id,
67
+ value, last_commit_id, updated_at
68
+ )
69
+ SELECT project_id, 'edge', jsonb_build_array(
70
+ source_type_id, source_primary_id, link_id, target_type_id, target_primary_id
71
+ ),
72
+ source_type_id, source_primary_id, link_id, target_type_id, target_primary_id,
73
+ value, last_commit_id, updated_at
74
+ FROM ontology_overrides
75
+ WHERE entity_kind = 'link';
76
+
77
+ -- Storage migrations do not have access to the ontology cardinality. Derive a slot candidate for
78
+ -- every legacy link scope; the runtime selects edge or slot authority from the registered ontology.
79
+ -- A single upsert is an unambiguous set and a delete-only scope is an unambiguous clear. Multiple
80
+ -- upserts are preserved as an explicit conflict instead of selecting a target arbitrarily.
81
+ WITH scopes AS (
82
+ SELECT project_id, source_type_id, source_primary_id, link_id,
83
+ COUNT(*) FILTER (WHERE value->>'kind' = 'upsert') AS upsert_count
84
+ FROM ontology_overrides
85
+ WHERE entity_kind = 'link'
86
+ GROUP BY project_id, source_type_id, source_primary_id, link_id
87
+ ), selected AS (
88
+ SELECT scopes.*, COALESCE(upsert.entity_key, anchor.entity_key) AS selected_key
89
+ FROM scopes
90
+ LEFT JOIN LATERAL (
91
+ SELECT candidate.entity_key
92
+ FROM ontology_overrides AS candidate
93
+ WHERE candidate.project_id = scopes.project_id
94
+ AND candidate.entity_kind = 'link'
95
+ AND candidate.source_type_id = scopes.source_type_id
96
+ AND candidate.source_primary_id = scopes.source_primary_id
97
+ AND candidate.link_id = scopes.link_id
98
+ AND candidate.value->>'kind' = 'upsert'
99
+ ORDER BY candidate.updated_at DESC, candidate.entity_sort_key DESC
100
+ LIMIT 1
101
+ ) AS upsert ON true
102
+ LEFT JOIN LATERAL (
103
+ SELECT candidate.entity_key
104
+ FROM ontology_overrides AS candidate
105
+ WHERE candidate.project_id = scopes.project_id
106
+ AND candidate.entity_kind = 'link'
107
+ AND candidate.source_type_id = scopes.source_type_id
108
+ AND candidate.source_primary_id = scopes.source_primary_id
109
+ AND candidate.link_id = scopes.link_id
110
+ ORDER BY candidate.updated_at DESC, candidate.entity_sort_key DESC
111
+ LIMIT 1
112
+ ) AS anchor ON true
113
+ )
114
+ INSERT INTO ontology_link_overrides (
115
+ project_id, identity_kind, identity_key,
116
+ source_type_id, source_primary_id, link_id, target_type_id, target_primary_id,
117
+ value, last_commit_id, updated_at
118
+ )
119
+ SELECT selected.project_id, 'slot',
120
+ jsonb_build_array(selected.source_type_id, selected.source_primary_id, selected.link_id),
121
+ selected.source_type_id, selected.source_primary_id, selected.link_id,
122
+ overrides.target_type_id, overrides.target_primary_id,
123
+ CASE
124
+ WHEN selected.upsert_count > 1 THEN jsonb_build_object('kind', 'legacy-conflict')
125
+ WHEN selected.upsert_count = 1 THEN
126
+ jsonb_build_object(
127
+ 'kind', 'set',
128
+ 'target', jsonb_build_object(
129
+ 'objectTypeId', overrides.target_type_id,
130
+ 'primaryId', overrides.target_primary_id
131
+ )
132
+ ) || CASE
133
+ WHEN overrides.value ? 'properties'
134
+ THEN jsonb_build_object('properties', overrides.value->'properties')
135
+ ELSE '{}'::jsonb
136
+ END
137
+ ELSE jsonb_build_object(
138
+ 'kind', 'clear',
139
+ 'target', jsonb_build_object(
140
+ 'objectTypeId', overrides.target_type_id,
141
+ 'primaryId', overrides.target_primary_id
142
+ )
143
+ )
144
+ END,
145
+ overrides.last_commit_id,
146
+ overrides.updated_at
147
+ FROM selected
148
+ JOIN ontology_overrides AS overrides
149
+ ON overrides.project_id = selected.project_id
150
+ AND overrides.entity_kind = 'link'
151
+ AND overrides.entity_key = selected.selected_key;
152
+
153
+ DROP TABLE ontology_overrides;
@@ -0,0 +1,7 @@
1
+ ALTER TABLE action_runs
2
+ ADD COLUMN execution_id TEXT NOT NULL,
3
+ ADD CONSTRAINT fk_action_runs_execution
4
+ FOREIGN KEY (project_id, execution_id)
5
+ REFERENCES executions (project_id, id)
6
+ ON DELETE RESTRICT,
7
+ ADD CONSTRAINT uq_action_runs_execution UNIQUE (project_id, execution_id);
@@ -0,0 +1,21 @@
1
+ ALTER TABLE agent_runs
2
+ ADD COLUMN execution_id TEXT NOT NULL,
3
+ ADD CONSTRAINT fk_agent_runs_execution
4
+ FOREIGN KEY (project_id, execution_id)
5
+ REFERENCES executions (project_id, id)
6
+ ON DELETE RESTRICT,
7
+ ADD CONSTRAINT uq_agent_runs_execution UNIQUE (project_id, execution_id),
8
+ DROP COLUMN requested_by_principal_type,
9
+ DROP COLUMN requested_by_principal_id,
10
+ DROP COLUMN execution_principal_type,
11
+ DROP COLUMN execution_principal_id;
12
+
13
+ ALTER TABLE workflow_agent_node_runs
14
+ ADD COLUMN execution_id TEXT NOT NULL,
15
+ ADD CONSTRAINT fk_workflow_agent_node_runs_execution
16
+ FOREIGN KEY (project_id, execution_id)
17
+ REFERENCES executions (project_id, id)
18
+ ON DELETE RESTRICT,
19
+ ADD CONSTRAINT uq_workflow_agent_node_runs_execution UNIQUE (project_id, execution_id),
20
+ DROP COLUMN execution_principal_type,
21
+ DROP COLUMN execution_principal_id;
@@ -0,0 +1,76 @@
1
+ ALTER TABLE agent_runs
2
+ ADD COLUMN requester_group_ids JSONB NOT NULL DEFAULT '[]'::jsonb;
3
+ ALTER TABLE agent_runs
4
+ ADD CONSTRAINT agent_runs_requester_group_ids_array
5
+ CHECK (jsonb_typeof(requester_group_ids) = 'array');
6
+
7
+ ALTER TABLE workflow_runs
8
+ ADD COLUMN requester_group_ids JSONB NOT NULL DEFAULT '[]'::jsonb;
9
+ ALTER TABLE workflow_runs
10
+ ADD CONSTRAINT workflow_runs_requester_group_ids_array
11
+ CHECK (jsonb_typeof(requester_group_ids) = 'array');
12
+
13
+ CREATE TABLE ai_model_call_usage (
14
+ project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
15
+ id TEXT NOT NULL CHECK (length(trim(id)) > 0),
16
+ execution_id TEXT NOT NULL CHECK (length(trim(execution_id)) > 0),
17
+ attempt BIGINT NOT NULL CHECK (attempt >= 1),
18
+ call_id TEXT NOT NULL CHECK (length(trim(call_id)) > 0),
19
+ provider_id TEXT NOT NULL CHECK (length(trim(provider_id)) > 0),
20
+ requested_model_id TEXT NOT NULL CHECK (length(trim(requested_model_id)) > 0),
21
+ response_model_id TEXT CHECK (
22
+ response_model_id IS NULL OR length(trim(response_model_id)) > 0
23
+ ),
24
+ response_id TEXT NOT NULL CHECK (length(trim(response_id)) > 0),
25
+ input_tokens BIGINT CHECK (input_tokens IS NULL OR input_tokens >= 0),
26
+ output_tokens BIGINT CHECK (output_tokens IS NULL OR output_tokens >= 0),
27
+ total_tokens BIGINT CHECK (total_tokens IS NULL OR total_tokens >= 0),
28
+ uncached_input_tokens BIGINT CHECK (
29
+ uncached_input_tokens IS NULL OR uncached_input_tokens >= 0
30
+ ),
31
+ cache_read_input_tokens BIGINT CHECK (
32
+ cache_read_input_tokens IS NULL OR cache_read_input_tokens >= 0
33
+ ),
34
+ cache_write_input_tokens BIGINT CHECK (
35
+ cache_write_input_tokens IS NULL OR cache_write_input_tokens >= 0
36
+ ),
37
+ text_output_tokens BIGINT CHECK (
38
+ text_output_tokens IS NULL OR text_output_tokens >= 0
39
+ ),
40
+ reasoning_output_tokens BIGINT CHECK (
41
+ reasoning_output_tokens IS NULL OR reasoning_output_tokens >= 0
42
+ ),
43
+ reporting_status TEXT NOT NULL CHECK (
44
+ reporting_status IN ('complete', 'partial', 'unavailable')
45
+ ),
46
+ raw_usage JSONB CHECK (raw_usage IS NULL OR jsonb_typeof(raw_usage) = 'object'),
47
+ occurred_at TIMESTAMPTZ NOT NULL,
48
+ recorded_at TIMESTAMPTZ NOT NULL,
49
+ PRIMARY KEY (project_id, id),
50
+ FOREIGN KEY (project_id, execution_id)
51
+ REFERENCES executions(project_id, id)
52
+ ON DELETE RESTRICT
53
+ );
54
+
55
+ CREATE UNIQUE INDEX idx_ai_model_call_usage_idempotency
56
+ ON ai_model_call_usage (project_id, execution_id, attempt, call_id, response_id);
57
+ CREATE INDEX idx_ai_model_call_usage_execution
58
+ ON ai_model_call_usage (project_id, execution_id, occurred_at, id);
59
+ CREATE INDEX idx_ai_model_call_usage_project_time
60
+ ON ai_model_call_usage (project_id, occurred_at, id);
61
+ CREATE INDEX idx_ai_model_call_usage_provider_response
62
+ ON ai_model_call_usage (project_id, provider_id, response_id);
63
+
64
+ CREATE TABLE ai_model_call_usage_groups (
65
+ project_id TEXT NOT NULL,
66
+ usage_record_id TEXT NOT NULL,
67
+ group_id TEXT NOT NULL,
68
+ occurred_at TIMESTAMPTZ NOT NULL,
69
+ PRIMARY KEY (project_id, usage_record_id, group_id),
70
+ FOREIGN KEY (project_id, usage_record_id)
71
+ REFERENCES ai_model_call_usage(project_id, id)
72
+ ON DELETE CASCADE
73
+ );
74
+
75
+ CREATE INDEX idx_ai_model_call_usage_groups_group_time
76
+ ON ai_model_call_usage_groups (project_id, group_id, occurred_at, usage_record_id);
@@ -0,0 +1,23 @@
1
+ ALTER TABLE sync_runs ADD COLUMN error JSONB;
2
+
3
+ UPDATE sync_runs
4
+ SET error = jsonb_build_object(
5
+ 'code', CASE WHEN status = 'cancelled' THEN 'runtime.cancelled' ELSE 'internal.unexpected' END,
6
+ 'message', CASE
7
+ WHEN status = 'cancelled' THEN 'Execution was cancelled.'
8
+ ELSE 'An unexpected internal error occurred.'
9
+ END,
10
+ 'retryable', false,
11
+ 'at', to_char(COALESCE(finished_at, started_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
12
+ 'details', jsonb_build_object(
13
+ 'syncId', sync_id,
14
+ 'runId', id,
15
+ 'datasetId', dataset_id,
16
+ 'migratedFromLegacyError', true
17
+ )
18
+ )
19
+ WHERE error_name IS NOT NULL OR error_message IS NOT NULL;
20
+
21
+ ALTER TABLE sync_runs
22
+ DROP COLUMN error_name,
23
+ DROP COLUMN error_message;
@@ -0,0 +1,47 @@
1
+ ALTER TABLE pipeline_runs ADD COLUMN error JSONB;
2
+
3
+ UPDATE pipeline_runs
4
+ SET error = jsonb_build_object(
5
+ 'code', CASE WHEN status = 'cancelled' THEN 'runtime.cancelled' ELSE 'internal.unexpected' END,
6
+ 'message', CASE
7
+ WHEN status = 'cancelled' THEN 'Execution was cancelled.'
8
+ ELSE 'An unexpected internal error occurred.'
9
+ END,
10
+ 'retryable', false,
11
+ 'at', to_char(COALESCE(finished_at, started_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
12
+ 'details', jsonb_build_object(
13
+ 'pipelineId', pipeline_id,
14
+ 'runId', id,
15
+ 'migratedFromLegacyError', true
16
+ )
17
+ )
18
+ WHERE error_name IS NOT NULL OR error_message IS NOT NULL;
19
+
20
+ ALTER TABLE pipeline_runs
21
+ DROP COLUMN error_name,
22
+ DROP COLUMN error_message;
23
+
24
+ ALTER TABLE pipeline_step_runs ADD COLUMN error JSONB;
25
+
26
+ UPDATE pipeline_step_runs
27
+ SET error = jsonb_build_object(
28
+ 'code', CASE WHEN status = 'cancelled' THEN 'runtime.cancelled' ELSE 'internal.unexpected' END,
29
+ 'message', CASE
30
+ WHEN status = 'cancelled' THEN 'Execution was cancelled.'
31
+ ELSE 'An unexpected internal error occurred.'
32
+ END,
33
+ 'retryable', false,
34
+ 'at', to_char(COALESCE(finished_at, started_at) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
35
+ 'details', jsonb_build_object(
36
+ 'pipelineId', pipeline_id,
37
+ 'pipelineRunId', pipeline_run_id,
38
+ 'stepId', step_id,
39
+ 'stepRunId', id,
40
+ 'migratedFromLegacyError', true
41
+ )
42
+ )
43
+ WHERE error_name IS NOT NULL OR error_message IS NOT NULL;
44
+
45
+ ALTER TABLE pipeline_step_runs
46
+ DROP COLUMN error_name,
47
+ DROP COLUMN error_message;