@sixb/pg 0.0.1

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 (152) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/agents/agent-storage.d.ts +20 -0
  4. package/dist/agents/agent-storage.d.ts.map +1 -0
  5. package/dist/agents/index.d.ts +6 -0
  6. package/dist/agents/index.d.ts.map +1 -0
  7. package/dist/agents/messages.d.ts +13 -0
  8. package/dist/agents/messages.d.ts.map +1 -0
  9. package/dist/agents/rows.d.ts +77 -0
  10. package/dist/agents/rows.d.ts.map +1 -0
  11. package/dist/agents/runs.d.ts +25 -0
  12. package/dist/agents/runs.d.ts.map +1 -0
  13. package/dist/agents/threads.d.ts +13 -0
  14. package/dist/agents/threads.d.ts.map +1 -0
  15. package/dist/auth-storage/access-tokens.d.ts +33 -0
  16. package/dist/auth-storage/access-tokens.d.ts.map +1 -0
  17. package/dist/auth-storage/auth-storage.d.ts +41 -0
  18. package/dist/auth-storage/auth-storage.d.ts.map +1 -0
  19. package/dist/auth-storage/group-memberships.d.ts +21 -0
  20. package/dist/auth-storage/group-memberships.d.ts.map +1 -0
  21. package/dist/auth-storage/identities.d.ts +17 -0
  22. package/dist/auth-storage/identities.d.ts.map +1 -0
  23. package/dist/auth-storage/index.d.ts +3 -0
  24. package/dist/auth-storage/index.d.ts.map +1 -0
  25. package/dist/auth-storage/invitations.d.ts +29 -0
  26. package/dist/auth-storage/invitations.d.ts.map +1 -0
  27. package/dist/auth-storage/magic-links.d.ts +28 -0
  28. package/dist/auth-storage/magic-links.d.ts.map +1 -0
  29. package/dist/auth-storage/oidc-attempts.d.ts +18 -0
  30. package/dist/auth-storage/oidc-attempts.d.ts.map +1 -0
  31. package/dist/auth-storage/rows.d.ts +138 -0
  32. package/dist/auth-storage/rows.d.ts.map +1 -0
  33. package/dist/auth-storage/service-account-group-memberships.d.ts +17 -0
  34. package/dist/auth-storage/service-account-group-memberships.d.ts.map +1 -0
  35. package/dist/auth-storage/service-accounts.d.ts +14 -0
  36. package/dist/auth-storage/service-accounts.d.ts.map +1 -0
  37. package/dist/auth-storage/sessions.d.ts +55 -0
  38. package/dist/auth-storage/sessions.d.ts.map +1 -0
  39. package/dist/auth-storage/shared.d.ts +159 -0
  40. package/dist/auth-storage/shared.d.ts.map +1 -0
  41. package/dist/auth-storage/users.d.ts +19 -0
  42. package/dist/auth-storage/users.d.ts.map +1 -0
  43. package/dist/index.d.ts +127 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +11957 -0
  46. package/dist/index.js.map +55 -0
  47. package/dist/latest-run-query.d.ts +24 -0
  48. package/dist/latest-run-query.d.ts.map +1 -0
  49. package/dist/migrations.d.ts +20 -0
  50. package/dist/migrations.d.ts.map +1 -0
  51. package/dist/ontology-storage/commits.d.ts +11 -0
  52. package/dist/ontology-storage/commits.d.ts.map +1 -0
  53. package/dist/ontology-storage/index.d.ts +22 -0
  54. package/dist/ontology-storage/index.d.ts.map +1 -0
  55. package/dist/ontology-storage/materialization-session.d.ts +63 -0
  56. package/dist/ontology-storage/materialization-session.d.ts.map +1 -0
  57. package/dist/ontology-storage/materialization-state.d.ts +69 -0
  58. package/dist/ontology-storage/materialization-state.d.ts.map +1 -0
  59. package/dist/ontology-storage/materialization-writer.d.ts +15 -0
  60. package/dist/ontology-storage/materialization-writer.d.ts.map +1 -0
  61. package/dist/ontology-storage/materializations.d.ts +33 -0
  62. package/dist/ontology-storage/materializations.d.ts.map +1 -0
  63. package/dist/ontology-storage/outbox.d.ts +12 -0
  64. package/dist/ontology-storage/outbox.d.ts.map +1 -0
  65. package/dist/ontology-storage/shared.d.ts +95 -0
  66. package/dist/ontology-storage/shared.d.ts.map +1 -0
  67. package/dist/ontology-storage/sources.d.ts +25 -0
  68. package/dist/ontology-storage/sources.d.ts.map +1 -0
  69. package/dist/pg-action-run-storage.d.ts +21 -0
  70. package/dist/pg-action-run-storage.d.ts.map +1 -0
  71. package/dist/pg-client.d.ts +58 -0
  72. package/dist/pg-client.d.ts.map +1 -0
  73. package/dist/pg-object-query-compiler.d.ts +55 -0
  74. package/dist/pg-object-query-compiler.d.ts.map +1 -0
  75. package/dist/pg-object-storage.d.ts +90 -0
  76. package/dist/pg-object-storage.d.ts.map +1 -0
  77. package/dist/pg-pipeline-run-storage.d.ts +18 -0
  78. package/dist/pg-pipeline-run-storage.d.ts.map +1 -0
  79. package/dist/pg-projection-run-storage.d.ts +19 -0
  80. package/dist/pg-projection-run-storage.d.ts.map +1 -0
  81. package/dist/pg-rules-storage.d.ts +25 -0
  82. package/dist/pg-rules-storage.d.ts.map +1 -0
  83. package/dist/pg-sync-run-storage.d.ts +15 -0
  84. package/dist/pg-sync-run-storage.d.ts.map +1 -0
  85. package/dist/pg-timeseries-storage.d.ts +32 -0
  86. package/dist/pg-timeseries-storage.d.ts.map +1 -0
  87. package/dist/pg-webhook-delivery-storage.d.ts +17 -0
  88. package/dist/pg-webhook-delivery-storage.d.ts.map +1 -0
  89. package/dist/pg-webhook-run-storage.d.ts +14 -0
  90. package/dist/pg-webhook-run-storage.d.ts.map +1 -0
  91. package/dist/pg-workflow-intervention-storage.d.ts +16 -0
  92. package/dist/pg-workflow-intervention-storage.d.ts.map +1 -0
  93. package/dist/pg-workflow-run-storage.d.ts +49 -0
  94. package/dist/pg-workflow-run-storage.d.ts.map +1 -0
  95. package/dist/run-list-query.d.ts +30 -0
  96. package/dist/run-list-query.d.ts.map +1 -0
  97. package/dist/storage-errors.d.ts +16 -0
  98. package/dist/storage-errors.d.ts.map +1 -0
  99. package/dist/transactions.d.ts +10 -0
  100. package/dist/transactions.d.ts.map +1 -0
  101. package/dist/tsconfig.json +5 -0
  102. package/package.json +60 -0
  103. package/src/agents/agent-storage.ts +26 -0
  104. package/src/agents/index.ts +5 -0
  105. package/src/agents/messages.ts +144 -0
  106. package/src/agents/rows.ts +227 -0
  107. package/src/agents/runs.ts +330 -0
  108. package/src/agents/threads.ts +116 -0
  109. package/src/auth-storage/access-tokens.ts +254 -0
  110. package/src/auth-storage/auth-storage.ts +798 -0
  111. package/src/auth-storage/group-memberships.ts +47 -0
  112. package/src/auth-storage/identities.ts +85 -0
  113. package/src/auth-storage/index.ts +2 -0
  114. package/src/auth-storage/invitations.ts +280 -0
  115. package/src/auth-storage/magic-links.ts +140 -0
  116. package/src/auth-storage/oidc-attempts.ts +93 -0
  117. package/src/auth-storage/rows.ts +352 -0
  118. package/src/auth-storage/service-account-group-memberships.ts +150 -0
  119. package/src/auth-storage/service-accounts.ts +154 -0
  120. package/src/auth-storage/sessions.ts +193 -0
  121. package/src/auth-storage/shared.ts +799 -0
  122. package/src/auth-storage/users.ts +196 -0
  123. package/src/index.ts +367 -0
  124. package/src/latest-run-query.ts +42 -0
  125. package/src/migrations/001-initial-schema.sql +1195 -0
  126. package/src/migrations.ts +280 -0
  127. package/src/ontology-storage/commits.ts +115 -0
  128. package/src/ontology-storage/index.ts +35 -0
  129. package/src/ontology-storage/materialization-session.ts +426 -0
  130. package/src/ontology-storage/materialization-state.ts +1109 -0
  131. package/src/ontology-storage/materialization-writer.ts +509 -0
  132. package/src/ontology-storage/materializations.ts +939 -0
  133. package/src/ontology-storage/outbox.ts +216 -0
  134. package/src/ontology-storage/shared.ts +293 -0
  135. package/src/ontology-storage/sources.ts +658 -0
  136. package/src/pg-action-run-storage.ts +657 -0
  137. package/src/pg-client.ts +124 -0
  138. package/src/pg-object-query-compiler.ts +1545 -0
  139. package/src/pg-object-storage.ts +638 -0
  140. package/src/pg-pipeline-run-storage.ts +457 -0
  141. package/src/pg-projection-run-storage.ts +526 -0
  142. package/src/pg-rules-storage.ts +308 -0
  143. package/src/pg-sync-run-storage.ts +263 -0
  144. package/src/pg-timeseries-storage.ts +180 -0
  145. package/src/pg-webhook-delivery-storage.ts +180 -0
  146. package/src/pg-webhook-run-storage.ts +185 -0
  147. package/src/pg-workflow-intervention-storage.ts +339 -0
  148. package/src/pg-workflow-run-storage.ts +1065 -0
  149. package/src/run-list-query.ts +108 -0
  150. package/src/sql.d.ts +4 -0
  151. package/src/storage-errors.ts +72 -0
  152. package/src/transactions.ts +58 -0
@@ -0,0 +1,1195 @@
1
+ CREATE TABLE objects (
2
+ project_id TEXT NOT NULL,
3
+ object_type_id TEXT NOT NULL,
4
+ primary_id TEXT NOT NULL,
5
+ properties JSONB NOT NULL,
6
+ created_at TIMESTAMPTZ NOT NULL,
7
+ updated_at TIMESTAMPTZ NOT NULL,
8
+ version INTEGER NOT NULL,
9
+ last_commit_id TEXT NOT NULL,
10
+ PRIMARY KEY (project_id, object_type_id, primary_id)
11
+ );
12
+
13
+ CREATE INDEX idx_objects_project_type
14
+ ON objects (project_id, object_type_id);
15
+ CREATE INDEX idx_objects_updated_at
16
+ ON objects (project_id, object_type_id, updated_at);
17
+ CREATE INDEX idx_objects_created_at
18
+ ON objects (project_id, object_type_id, created_at);
19
+ CREATE INDEX idx_objects_properties
20
+ ON objects USING GIN (properties);
21
+
22
+ CREATE TABLE links (
23
+ project_id TEXT NOT NULL,
24
+ source_type_id TEXT NOT NULL,
25
+ source_id TEXT NOT NULL,
26
+ link_id TEXT NOT NULL,
27
+ target_type_id TEXT NOT NULL,
28
+ target_id TEXT NOT NULL,
29
+ properties JSONB,
30
+ created_at TIMESTAMPTZ NOT NULL,
31
+ updated_at TIMESTAMPTZ NOT NULL,
32
+ last_commit_id TEXT NOT NULL,
33
+ PRIMARY KEY (project_id, source_type_id, source_id, link_id, target_type_id, target_id)
34
+ );
35
+
36
+ CREATE INDEX idx_links_source
37
+ ON links (project_id, source_type_id, source_id);
38
+ CREATE INDEX idx_links_target
39
+ ON links (project_id, target_type_id, target_id);
40
+
41
+ CREATE TABLE timeseries (
42
+ project_id TEXT NOT NULL,
43
+ object_type_id TEXT NOT NULL,
44
+ object_id TEXT NOT NULL,
45
+ property_id TEXT NOT NULL,
46
+ value JSONB NOT NULL,
47
+ unit TEXT,
48
+ at TIMESTAMPTZ NOT NULL,
49
+ last_commit_id TEXT NOT NULL,
50
+ -- A telemetry point's identity is (series, at): one value per instant per
51
+ -- series. Appends upsert on this key, so no separate idempotency ledger is
52
+ -- needed. This primary-key index serves exact point and ordered history reads;
53
+ -- `timeseries_latest` serves constant-cost latest-value reads.
54
+ PRIMARY KEY (project_id, object_type_id, object_id, property_id, at)
55
+ );
56
+
57
+ -- Incremental projection used by ontology materialization. Reading the latest value must not scan
58
+ -- the complete history of every telemetry property attached to an object.
59
+ CREATE TABLE timeseries_latest (
60
+ project_id TEXT NOT NULL,
61
+ object_type_id TEXT NOT NULL,
62
+ object_id TEXT NOT NULL,
63
+ property_id TEXT NOT NULL,
64
+ value JSONB NOT NULL,
65
+ unit TEXT,
66
+ at TIMESTAMPTZ NOT NULL,
67
+ last_commit_id TEXT NOT NULL,
68
+ PRIMARY KEY (project_id, object_type_id, object_id, property_id)
69
+ );
70
+
71
+ CREATE TABLE sync_runs (
72
+ project_id TEXT NOT NULL,
73
+ id TEXT NOT NULL,
74
+ sync_id TEXT NOT NULL,
75
+ dataset_id TEXT NOT NULL,
76
+ mode TEXT NOT NULL CHECK (mode IN ('snapshot', 'append')),
77
+ status TEXT NOT NULL CHECK (status IN ('running', 'succeeded', 'failed', 'cancelled')),
78
+ started_at TIMESTAMPTZ NOT NULL,
79
+ finished_at TIMESTAMPTZ,
80
+ rows_read INTEGER CHECK (rows_read IS NULL OR rows_read >= 0),
81
+ output_version_id TEXT,
82
+ expected_latest_version_id TEXT,
83
+ commit_message TEXT,
84
+ error_name TEXT,
85
+ error_message TEXT,
86
+ checkpoint JSONB,
87
+ PRIMARY KEY (project_id, id)
88
+ );
89
+
90
+ CREATE INDEX idx_sync_runs_project_started
91
+ ON sync_runs (project_id, started_at DESC);
92
+ CREATE INDEX idx_sync_runs_project_dataset_started
93
+ ON sync_runs (project_id, dataset_id, started_at DESC);
94
+ CREATE INDEX idx_sync_runs_project_sync_started
95
+ ON sync_runs (project_id, sync_id, started_at DESC);
96
+ CREATE INDEX idx_sync_runs_project_status_started
97
+ ON sync_runs (project_id, status, started_at DESC);
98
+
99
+ CREATE TABLE pipeline_runs (
100
+ project_id TEXT NOT NULL,
101
+ id TEXT NOT NULL,
102
+ pipeline_id TEXT NOT NULL,
103
+ status TEXT NOT NULL CHECK (status IN ('running', 'succeeded', 'failed', 'cancelled')),
104
+ started_at TIMESTAMPTZ NOT NULL,
105
+ finished_at TIMESTAMPTZ,
106
+ output_dataset_id TEXT,
107
+ output_version_id TEXT,
108
+ error_name TEXT,
109
+ error_message TEXT,
110
+ PRIMARY KEY (project_id, id)
111
+ );
112
+
113
+ CREATE INDEX idx_pipeline_runs_project_started
114
+ ON pipeline_runs (project_id, started_at DESC);
115
+ CREATE INDEX idx_pipeline_runs_project_pipeline_started
116
+ ON pipeline_runs (project_id, pipeline_id, started_at DESC);
117
+ CREATE INDEX idx_pipeline_runs_project_status_started
118
+ ON pipeline_runs (project_id, status, started_at DESC);
119
+
120
+ CREATE TABLE pipeline_step_runs (
121
+ project_id TEXT NOT NULL,
122
+ id TEXT NOT NULL,
123
+ pipeline_run_id TEXT NOT NULL,
124
+ pipeline_id TEXT NOT NULL,
125
+ step_id TEXT NOT NULL,
126
+ dataset_id TEXT NOT NULL,
127
+ mode TEXT NOT NULL CHECK (mode IN ('snapshot', 'append')),
128
+ status TEXT NOT NULL CHECK (status IN ('running', 'succeeded', 'failed', 'cancelled')),
129
+ started_at TIMESTAMPTZ NOT NULL,
130
+ finished_at TIMESTAMPTZ,
131
+ inputs JSONB NOT NULL,
132
+ output_version_id TEXT,
133
+ rows_written INTEGER CHECK (rows_written IS NULL OR rows_written >= 0),
134
+ error_name TEXT,
135
+ error_message TEXT,
136
+ PRIMARY KEY (project_id, id)
137
+ );
138
+
139
+ CREATE TABLE projection_runs (
140
+ project_id TEXT NOT NULL,
141
+ id TEXT NOT NULL,
142
+ projection_id TEXT NOT NULL,
143
+ projection_kind TEXT NOT NULL CHECK (projection_kind IN ('object', 'link', 'telemetry')),
144
+ materialization_protocol TEXT NOT NULL CHECK (
145
+ materialization_protocol IN ('replacement', 'telemetry')
146
+ ),
147
+ dataset_id TEXT NOT NULL,
148
+ dataset_version_id TEXT NOT NULL,
149
+ dataset_version_created_at TEXT NOT NULL,
150
+ ontology_revision TEXT NOT NULL,
151
+ projection_revision TEXT NOT NULL,
152
+ ownership_hash TEXT NOT NULL,
153
+ object_type_id TEXT,
154
+ source_object_type_id TEXT,
155
+ target_object_type_id TEXT,
156
+ status TEXT NOT NULL CHECK (status IN ('running', 'succeeded', 'failed', 'cancelled')),
157
+ started_at TIMESTAMPTZ NOT NULL,
158
+ finished_at TIMESTAMPTZ,
159
+ attempt BIGINT NOT NULL CHECK (attempt >= 1),
160
+ execution_token TEXT,
161
+ fixed_batch_size BIGINT CHECK (fixed_batch_size IS NULL OR fixed_batch_size > 0),
162
+ next_batch_ordinal BIGINT CHECK (next_batch_ordinal IS NULL OR next_batch_ordinal >= 0),
163
+ next_row_offset BIGINT CHECK (next_row_offset IS NULL OR next_row_offset >= 0),
164
+ input_exhausted BOOLEAN,
165
+ missing_target_object_type_id TEXT,
166
+ missing_target_object_id TEXT,
167
+ missing_target_batch_ordinal BIGINT CHECK (
168
+ missing_target_batch_ordinal IS NULL OR missing_target_batch_ordinal >= 0
169
+ ),
170
+ missing_target_first_seen_at TIMESTAMPTZ,
171
+ source_rows_read BIGINT NOT NULL DEFAULT 0 CHECK (source_rows_read >= 0),
172
+ source_rows_skipped BIGINT NOT NULL DEFAULT 0 CHECK (source_rows_skipped >= 0),
173
+ error_message TEXT,
174
+ PRIMARY KEY (project_id, id),
175
+ CHECK (source_rows_skipped <= source_rows_read),
176
+ CHECK ((status = 'running') = (finished_at IS NULL)),
177
+ CHECK ((status = 'running') = (execution_token IS NOT NULL)),
178
+ CHECK ((projection_kind = 'telemetry') = (materialization_protocol = 'telemetry')),
179
+ CHECK (
180
+ (
181
+ projection_kind = 'link'
182
+ AND object_type_id IS NULL
183
+ AND source_object_type_id IS NOT NULL
184
+ AND target_object_type_id IS NOT NULL
185
+ )
186
+ OR
187
+ (
188
+ projection_kind IN ('object', 'telemetry')
189
+ AND object_type_id IS NOT NULL
190
+ AND source_object_type_id IS NULL
191
+ AND target_object_type_id IS NULL
192
+ )
193
+ ),
194
+ CHECK (
195
+ (
196
+ materialization_protocol = 'replacement'
197
+ AND fixed_batch_size IS NULL
198
+ AND next_batch_ordinal IS NULL
199
+ AND next_row_offset IS NULL
200
+ AND input_exhausted IS NULL
201
+ )
202
+ OR
203
+ (
204
+ materialization_protocol = 'telemetry'
205
+ AND fixed_batch_size IS NOT NULL
206
+ AND next_batch_ordinal IS NOT NULL
207
+ AND next_row_offset IS NOT NULL
208
+ AND input_exhausted IS NOT NULL
209
+ )
210
+ ),
211
+ CHECK (
212
+ (
213
+ missing_target_object_type_id IS NULL
214
+ AND missing_target_object_id IS NULL
215
+ AND missing_target_batch_ordinal IS NULL
216
+ AND missing_target_first_seen_at IS NULL
217
+ )
218
+ OR
219
+ (
220
+ projection_kind = 'telemetry'
221
+ AND missing_target_object_type_id = object_type_id
222
+ AND missing_target_object_id IS NOT NULL
223
+ AND missing_target_batch_ordinal = next_batch_ordinal
224
+ AND missing_target_first_seen_at IS NOT NULL
225
+ )
226
+ ),
227
+ CHECK (projection_kind != 'telemetry' OR status != 'succeeded' OR input_exhausted)
228
+ );
229
+
230
+ CREATE INDEX idx_pipeline_step_runs_project_started
231
+ ON pipeline_step_runs (project_id, started_at DESC);
232
+ CREATE INDEX idx_pipeline_step_runs_project_run_started
233
+ ON pipeline_step_runs (project_id, pipeline_run_id, started_at DESC);
234
+ CREATE INDEX idx_pipeline_step_runs_project_pipeline_started
235
+ ON pipeline_step_runs (project_id, pipeline_id, started_at DESC);
236
+ CREATE INDEX idx_pipeline_step_runs_project_step_started
237
+ ON pipeline_step_runs (project_id, step_id, started_at DESC);
238
+ CREATE INDEX idx_pipeline_step_runs_project_dataset_started
239
+ ON pipeline_step_runs (project_id, dataset_id, started_at DESC);
240
+ CREATE INDEX idx_pipeline_step_runs_project_status_started
241
+ ON pipeline_step_runs (project_id, status, started_at DESC);
242
+
243
+ CREATE INDEX idx_projection_runs_project_started
244
+ ON projection_runs (project_id, started_at DESC);
245
+ CREATE INDEX idx_projection_runs_project_projection_started
246
+ ON projection_runs (project_id, projection_id, started_at DESC);
247
+ CREATE INDEX idx_projection_runs_project_dataset_started
248
+ ON projection_runs (project_id, dataset_id, started_at DESC);
249
+ CREATE INDEX idx_projection_runs_project_version_started
250
+ ON projection_runs (project_id, dataset_version_id, started_at DESC);
251
+ CREATE INDEX idx_projection_runs_project_status_started
252
+ ON projection_runs (project_id, status, started_at DESC);
253
+ CREATE INDEX idx_projection_runs_project_object_type_started
254
+ ON projection_runs (project_id, object_type_id, started_at DESC);
255
+
256
+ -- Ontology tables deliberately omit IF NOT EXISTS. A fresh installation is the only supported
257
+ -- schema for this coordinated breaking release; an untracked legacy schema must fail atomically.
258
+ CREATE TABLE ontology_commits (
259
+ project_id TEXT NOT NULL,
260
+ id TEXT NOT NULL,
261
+ idempotency_key TEXT NOT NULL,
262
+ request_hash TEXT NOT NULL,
263
+ origin_kind TEXT NOT NULL CHECK (
264
+ origin_kind IN ('action', 'runtime', 'projection', 'telemetry')
265
+ ),
266
+ origin_run_id TEXT,
267
+ origin_batch_ordinal BIGINT CHECK (
268
+ origin_batch_ordinal IS NULL OR origin_batch_ordinal >= 0
269
+ ),
270
+ origin JSONB NOT NULL,
271
+ actor JSONB,
272
+ ontology_revision TEXT NOT NULL,
273
+ projection_revision TEXT,
274
+ ownership_hash TEXT,
275
+ intent JSONB NOT NULL,
276
+ result JSONB NOT NULL,
277
+ committed_at TIMESTAMPTZ NOT NULL,
278
+ PRIMARY KEY (project_id, id),
279
+ UNIQUE (project_id, idempotency_key),
280
+ CHECK (
281
+ (
282
+ origin_kind IN ('action', 'projection')
283
+ AND origin_run_id IS NOT NULL
284
+ AND origin_batch_ordinal IS NULL
285
+ )
286
+ OR (
287
+ origin_kind = 'telemetry'
288
+ AND (
289
+ (origin_run_id IS NULL AND origin_batch_ordinal IS NULL)
290
+ OR (origin_run_id IS NOT NULL AND origin_batch_ordinal IS NOT NULL)
291
+ )
292
+ )
293
+ OR (
294
+ origin_kind = 'runtime'
295
+ AND origin_run_id IS NULL
296
+ AND origin_batch_ordinal IS NULL
297
+ )
298
+ )
299
+ );
300
+
301
+ CREATE UNIQUE INDEX idx_ontology_commits_action_origin
302
+ ON ontology_commits (project_id, origin_run_id)
303
+ WHERE origin_kind = 'action';
304
+ CREATE UNIQUE INDEX idx_ontology_commits_projection_origin
305
+ ON ontology_commits (project_id, origin_run_id)
306
+ WHERE origin_kind = 'projection';
307
+ CREATE UNIQUE INDEX idx_ontology_commits_telemetry_origin
308
+ ON ontology_commits (project_id, origin_run_id, origin_batch_ordinal)
309
+ WHERE origin_kind = 'telemetry' AND origin_run_id IS NOT NULL;
310
+ CREATE INDEX idx_ontology_commits_history
311
+ ON ontology_commits (project_id, committed_at, id);
312
+ CREATE INDEX idx_ontology_commits_origin
313
+ ON ontology_commits (project_id, origin_kind, origin_run_id, origin_batch_ordinal);
314
+
315
+ CREATE TABLE ontology_sources (
316
+ project_id TEXT NOT NULL,
317
+ source_id TEXT NOT NULL,
318
+ materialization_id TEXT NOT NULL,
319
+ projection_run_id TEXT NOT NULL,
320
+ projection_kind TEXT NOT NULL CHECK (projection_kind IN ('object', 'link')),
321
+ protocol TEXT NOT NULL CHECK (protocol = 'replacement'),
322
+ status TEXT NOT NULL CHECK (
323
+ status IN ('staging', 'ready', 'active', 'superseded', 'abandoned')
324
+ ),
325
+ execution_token TEXT,
326
+ dataset_id TEXT NOT NULL,
327
+ dataset_version_id TEXT NOT NULL,
328
+ dataset_version_created_at TIMESTAMPTZ NOT NULL,
329
+ projection_revision TEXT NOT NULL,
330
+ ownership_hash TEXT NOT NULL,
331
+ ontology_revision TEXT NOT NULL,
332
+ root_count BIGINT CHECK (root_count IS NULL OR root_count >= 0),
333
+ assertion_count BIGINT CHECK (assertion_count IS NULL OR assertion_count >= 0),
334
+ created_at TIMESTAMPTZ NOT NULL,
335
+ ready_at TIMESTAMPTZ,
336
+ activated_at TIMESTAMPTZ,
337
+ terminal_at TIMESTAMPTZ,
338
+ last_commit_id TEXT,
339
+ updated_at TIMESTAMPTZ NOT NULL,
340
+ PRIMARY KEY (project_id, source_id, materialization_id),
341
+ CHECK ((root_count IS NULL) = (assertion_count IS NULL)),
342
+ CHECK ((root_count IS NULL) = (ready_at IS NULL)),
343
+ CHECK ((status IN ('staging', 'ready')) = (execution_token IS NOT NULL)),
344
+ CHECK (status <> 'staging' OR ready_at IS NULL),
345
+ CHECK (status NOT IN ('ready', 'active', 'superseded') OR ready_at IS NOT NULL),
346
+ CHECK ((status IN ('active', 'superseded')) = (activated_at IS NOT NULL)),
347
+ CHECK ((status IN ('active', 'superseded')) = (last_commit_id IS NOT NULL)),
348
+ CHECK ((status IN ('superseded', 'abandoned')) = (terminal_at IS NOT NULL)),
349
+ CHECK (ready_at IS NULL OR created_at <= ready_at),
350
+ CHECK (activated_at IS NULL OR (ready_at IS NOT NULL AND ready_at <= activated_at)),
351
+ CHECK (terminal_at IS NULL OR created_at <= terminal_at),
352
+ CHECK (terminal_at IS NULL OR ready_at IS NULL OR ready_at <= terminal_at),
353
+ CHECK (terminal_at IS NULL OR activated_at IS NULL OR activated_at <= terminal_at),
354
+ CHECK (created_at <= updated_at)
355
+ );
356
+
357
+ CREATE UNIQUE INDEX idx_ontology_sources_active
358
+ ON ontology_sources (project_id, source_id)
359
+ WHERE status = 'active';
360
+ CREATE UNIQUE INDEX idx_ontology_sources_run_candidate
361
+ ON ontology_sources (project_id, projection_run_id)
362
+ WHERE status IN ('staging', 'ready');
363
+ CREATE INDEX idx_ontology_sources_cleanup
364
+ ON ontology_sources (project_id, status, terminal_at, source_id, materialization_id);
365
+ CREATE INDEX idx_ontology_sources_run
366
+ ON ontology_sources (project_id, projection_run_id);
367
+
368
+ CREATE TABLE ontology_source_rows (
369
+ project_id TEXT NOT NULL,
370
+ source_id TEXT NOT NULL,
371
+ materialization_id TEXT NOT NULL,
372
+ entity_kind TEXT NOT NULL CHECK (entity_kind IN ('object', 'link')),
373
+ entity_key JSONB NOT NULL,
374
+ entity_sort_key TEXT COLLATE "C" NOT NULL,
375
+ root_kind TEXT NOT NULL CHECK (root_kind IN ('object', 'link')),
376
+ root_key JSONB NOT NULL,
377
+ root_sort_key TEXT COLLATE "C" NOT NULL,
378
+ staging_ordinal BIGINT NOT NULL CHECK (staging_ordinal >= 0),
379
+ root JSONB NOT NULL,
380
+ assertion JSONB NOT NULL,
381
+ object_type_id TEXT,
382
+ primary_id TEXT,
383
+ source_type_id TEXT,
384
+ source_primary_id TEXT,
385
+ link_id TEXT,
386
+ target_type_id TEXT,
387
+ target_primary_id TEXT,
388
+ root_object_type_id TEXT,
389
+ root_primary_id TEXT,
390
+ root_source_type_id TEXT,
391
+ root_source_primary_id TEXT,
392
+ root_link_id TEXT,
393
+ root_target_type_id TEXT,
394
+ root_target_primary_id TEXT,
395
+ PRIMARY KEY (project_id, source_id, materialization_id, entity_kind, entity_key),
396
+ FOREIGN KEY (project_id, source_id, materialization_id)
397
+ REFERENCES ontology_sources (project_id, source_id, materialization_id)
398
+ ON DELETE RESTRICT,
399
+ CHECK (
400
+ (
401
+ entity_kind = 'object'
402
+ AND object_type_id IS NOT NULL
403
+ AND primary_id IS NOT NULL
404
+ AND source_type_id IS NULL
405
+ AND source_primary_id IS NULL
406
+ AND link_id IS NULL
407
+ AND target_type_id IS NULL
408
+ AND target_primary_id IS NULL
409
+ )
410
+ OR
411
+ (
412
+ entity_kind = 'link'
413
+ AND object_type_id IS NULL
414
+ AND primary_id IS NULL
415
+ AND source_type_id IS NOT NULL
416
+ AND source_primary_id IS NOT NULL
417
+ AND link_id IS NOT NULL
418
+ AND target_type_id IS NOT NULL
419
+ AND target_primary_id IS NOT NULL
420
+ )
421
+ ),
422
+ CHECK (
423
+ (
424
+ root_kind = 'object'
425
+ AND root_object_type_id IS NOT NULL
426
+ AND root_primary_id IS NOT NULL
427
+ AND root_source_type_id IS NULL
428
+ AND root_source_primary_id IS NULL
429
+ AND root_link_id IS NULL
430
+ AND root_target_type_id IS NULL
431
+ AND root_target_primary_id IS NULL
432
+ )
433
+ OR
434
+ (
435
+ root_kind = 'link'
436
+ AND root_object_type_id IS NULL
437
+ AND root_primary_id IS NULL
438
+ AND root_source_type_id IS NOT NULL
439
+ AND root_source_primary_id IS NOT NULL
440
+ AND root_link_id IS NOT NULL
441
+ AND root_target_type_id IS NOT NULL
442
+ AND root_target_primary_id IS NOT NULL
443
+ )
444
+ )
445
+ );
446
+
447
+ CREATE INDEX idx_ontology_source_rows_root
448
+ ON ontology_source_rows (
449
+ project_id, source_id, materialization_id, root_sort_key, staging_ordinal, entity_sort_key
450
+ );
451
+ CREATE INDEX idx_ontology_source_rows_staging_ordinal
452
+ ON ontology_source_rows (project_id, source_id, materialization_id, staging_ordinal);
453
+ CREATE INDEX idx_ontology_source_rows_entity_sort
454
+ ON ontology_source_rows (project_id, source_id, materialization_id, entity_sort_key);
455
+ CREATE INDEX idx_ontology_source_rows_object
456
+ ON ontology_source_rows (project_id, object_type_id, primary_id)
457
+ WHERE entity_kind = 'object';
458
+ CREATE INDEX idx_ontology_source_rows_link_source
459
+ ON ontology_source_rows (
460
+ project_id, source_type_id, source_primary_id, link_id,
461
+ target_type_id, target_primary_id
462
+ )
463
+ WHERE entity_kind = 'link';
464
+ CREATE INDEX idx_ontology_source_rows_link_target
465
+ ON ontology_source_rows (project_id, target_type_id, target_primary_id)
466
+ WHERE entity_kind = 'link';
467
+
468
+ CREATE TABLE ontology_overrides (
469
+ project_id TEXT NOT NULL,
470
+ entity_kind TEXT NOT NULL CHECK (entity_kind IN ('object', 'link')),
471
+ entity_key JSONB NOT NULL,
472
+ entity_sort_key TEXT COLLATE "C" NOT NULL,
473
+ object_type_id TEXT,
474
+ primary_id TEXT,
475
+ source_type_id TEXT,
476
+ source_primary_id TEXT,
477
+ link_id TEXT,
478
+ target_type_id TEXT,
479
+ target_primary_id TEXT,
480
+ value JSONB NOT NULL,
481
+ last_commit_id TEXT NOT NULL,
482
+ updated_at TIMESTAMPTZ NOT NULL,
483
+ PRIMARY KEY (project_id, entity_kind, entity_key),
484
+ CHECK (
485
+ (
486
+ entity_kind = 'object'
487
+ AND object_type_id IS NOT NULL
488
+ AND primary_id IS NOT NULL
489
+ AND source_type_id IS NULL
490
+ AND source_primary_id IS NULL
491
+ AND link_id IS NULL
492
+ AND target_type_id IS NULL
493
+ AND target_primary_id IS NULL
494
+ )
495
+ OR
496
+ (
497
+ entity_kind = 'link'
498
+ AND object_type_id IS NULL
499
+ AND primary_id IS NULL
500
+ AND source_type_id IS NOT NULL
501
+ AND source_primary_id IS NOT NULL
502
+ AND link_id IS NOT NULL
503
+ AND target_type_id IS NOT NULL
504
+ AND target_primary_id IS NOT NULL
505
+ )
506
+ )
507
+ );
508
+
509
+ CREATE INDEX idx_ontology_overrides_link_source
510
+ ON ontology_overrides (
511
+ project_id, source_type_id, source_primary_id, link_id,
512
+ target_type_id, target_primary_id
513
+ )
514
+ WHERE entity_kind = 'link';
515
+ CREATE INDEX idx_ontology_overrides_link_target
516
+ ON ontology_overrides (project_id, target_type_id, target_primary_id)
517
+ WHERE entity_kind = 'link';
518
+ CREATE INDEX idx_ontology_overrides_object
519
+ ON ontology_overrides (project_id, object_type_id, primary_id)
520
+ WHERE entity_kind = 'object';
521
+
522
+ CREATE TABLE ontology_outbox (
523
+ project_id TEXT NOT NULL,
524
+ id TEXT NOT NULL,
525
+ commit_id TEXT NOT NULL,
526
+ commit_ordinal BIGINT NOT NULL CHECK (commit_ordinal >= 0),
527
+ envelope JSONB NOT NULL,
528
+ available_at TIMESTAMPTZ NOT NULL,
529
+ attempts BIGINT NOT NULL DEFAULT 0 CHECK (attempts >= 0),
530
+ lease_id TEXT,
531
+ lease_expires_at TIMESTAMPTZ,
532
+ published_at TIMESTAMPTZ,
533
+ last_error TEXT,
534
+ created_at TIMESTAMPTZ NOT NULL,
535
+ PRIMARY KEY (project_id, id),
536
+ UNIQUE (project_id, commit_id, commit_ordinal),
537
+ CHECK ((lease_id IS NULL) = (lease_expires_at IS NULL))
538
+ );
539
+
540
+ CREATE INDEX idx_ontology_outbox_claim
541
+ ON ontology_outbox (project_id, available_at, lease_expires_at, created_at, commit_id, commit_ordinal)
542
+ WHERE published_at IS NULL;
543
+ CREATE INDEX idx_ontology_outbox_published
544
+ ON ontology_outbox (project_id, published_at, id)
545
+ WHERE published_at IS NOT NULL;
546
+
547
+ CREATE TABLE workflow_runs (
548
+ project_id TEXT NOT NULL,
549
+ id TEXT NOT NULL,
550
+ workflow_id TEXT NOT NULL,
551
+ status TEXT NOT NULL CHECK (
552
+ status IN ('queued', 'running', 'waiting', 'succeeded', 'failed', 'cancelled')
553
+ ),
554
+ input JSONB NOT NULL,
555
+ queued_at TIMESTAMPTZ,
556
+ started_at TIMESTAMPTZ NOT NULL,
557
+ finished_at TIMESTAMPTZ,
558
+ error TEXT,
559
+ source JSONB,
560
+ attempt INTEGER NOT NULL DEFAULT 0 CHECK (attempt >= 0),
561
+ execution_token TEXT,
562
+ execution_queue_lease_expires_at TIMESTAMPTZ,
563
+ requested_by_principal_type TEXT NOT NULL DEFAULT 'system'
564
+ CHECK (requested_by_principal_type IN ('user', 'serviceAccount', 'system')),
565
+ requested_by_principal_id TEXT NOT NULL DEFAULT 'system',
566
+ CHECK ((execution_token IS NULL) = (execution_queue_lease_expires_at IS NULL)),
567
+ PRIMARY KEY (project_id, id)
568
+ );
569
+
570
+ CREATE INDEX idx_workflow_runs_project_started
571
+ ON workflow_runs (project_id, started_at DESC);
572
+ CREATE INDEX idx_workflow_runs_project_workflow_started
573
+ ON workflow_runs (project_id, workflow_id, started_at DESC);
574
+ CREATE INDEX idx_workflow_runs_project_status_started
575
+ ON workflow_runs (project_id, status, started_at DESC);
576
+
577
+ CREATE TABLE workflow_node_runs (
578
+ project_id TEXT NOT NULL,
579
+ id TEXT NOT NULL,
580
+ workflow_run_id TEXT NOT NULL,
581
+ workflow_id TEXT NOT NULL,
582
+ node_index INTEGER NOT NULL CHECK (node_index >= 0),
583
+ node_type TEXT NOT NULL CHECK (node_type IN ('step', 'action', 'intervention', 'agent')),
584
+ node_id TEXT NOT NULL,
585
+ node_key TEXT NOT NULL,
586
+ status TEXT NOT NULL CHECK (status IN ('running', 'waiting', 'succeeded', 'failed', 'cancelled')),
587
+ input JSONB NOT NULL,
588
+ started_at TIMESTAMPTZ NOT NULL,
589
+ finished_at TIMESTAMPTZ,
590
+ output JSONB,
591
+ error TEXT,
592
+ PRIMARY KEY (project_id, id)
593
+ );
594
+
595
+ CREATE INDEX idx_workflow_node_runs_project_started
596
+ ON workflow_node_runs (project_id, started_at DESC);
597
+ CREATE INDEX idx_workflow_node_runs_project_run_started
598
+ ON workflow_node_runs (project_id, workflow_run_id, started_at DESC);
599
+ CREATE INDEX idx_workflow_node_runs_project_workflow_started
600
+ ON workflow_node_runs (project_id, workflow_id, started_at DESC);
601
+ CREATE INDEX idx_workflow_node_runs_project_node_started
602
+ ON workflow_node_runs (project_id, node_id, started_at DESC);
603
+ CREATE INDEX idx_workflow_node_runs_project_key_started
604
+ ON workflow_node_runs (project_id, node_key, started_at DESC);
605
+ CREATE INDEX idx_workflow_node_runs_project_status_started
606
+ ON workflow_node_runs (project_id, status, started_at DESC);
607
+
608
+ CREATE TABLE IF NOT EXISTS workflow_agent_node_runs (
609
+ project_id TEXT NOT NULL,
610
+ node_run_id TEXT NOT NULL,
611
+ agent_id TEXT NOT NULL,
612
+ status TEXT NOT NULL CHECK (status IN ('queued', 'running', 'succeeded', 'failed', 'cancelled')),
613
+ prompt TEXT NOT NULL,
614
+ execution_principal_type TEXT
615
+ CHECK (execution_principal_type IS NULL OR execution_principal_type = 'serviceAccount'),
616
+ execution_principal_id TEXT,
617
+ model_id TEXT,
618
+ finish_reason TEXT,
619
+ usage JSONB,
620
+ trace JSONB,
621
+ diagnostics JSONB,
622
+ error TEXT,
623
+ attempt INTEGER NOT NULL DEFAULT 0 CHECK (attempt >= 0),
624
+ execution_token TEXT,
625
+ execution_queue_lease_expires_at TIMESTAMPTZ,
626
+ created_at TIMESTAMPTZ NOT NULL,
627
+ started_at TIMESTAMPTZ,
628
+ completed_at TIMESTAMPTZ,
629
+ PRIMARY KEY (project_id, node_run_id),
630
+ FOREIGN KEY (project_id, node_run_id)
631
+ REFERENCES workflow_node_runs (project_id, id)
632
+ ON DELETE CASCADE,
633
+ CHECK ((execution_principal_type IS NULL) = (execution_principal_id IS NULL)),
634
+ CHECK ((execution_token IS NULL) = (execution_queue_lease_expires_at IS NULL))
635
+ );
636
+
637
+ CREATE INDEX IF NOT EXISTS idx_workflow_agent_node_runs_project_agent_created
638
+ ON workflow_agent_node_runs (project_id, agent_id, created_at DESC);
639
+ CREATE INDEX IF NOT EXISTS idx_workflow_agent_node_runs_project_status_created
640
+ ON workflow_agent_node_runs (project_id, status, created_at DESC);
641
+
642
+ CREATE TABLE workflow_interventions (
643
+ project_id TEXT NOT NULL,
644
+ id TEXT NOT NULL,
645
+ workflow_id TEXT NOT NULL,
646
+ workflow_run_id TEXT NOT NULL,
647
+ node_run_id TEXT NOT NULL,
648
+ node_index INTEGER NOT NULL CHECK (node_index >= 0),
649
+ node_id TEXT NOT NULL,
650
+ node_key TEXT NOT NULL,
651
+ intervention_id TEXT NOT NULL,
652
+ input JSONB NOT NULL,
653
+ default_response JSONB NOT NULL,
654
+ status TEXT NOT NULL CHECK (status IN ('pending', 'submitted', 'cancelled', 'expired')),
655
+ requested_at TIMESTAMPTZ NOT NULL,
656
+ expires_at TIMESTAMPTZ,
657
+ submitted_at TIMESTAMPTZ,
658
+ submitted_by JSONB,
659
+ response JSONB,
660
+ cancelled_at TIMESTAMPTZ,
661
+ cancelled_by JSONB,
662
+ expired_at TIMESTAMPTZ,
663
+ PRIMARY KEY (project_id, id)
664
+ );
665
+
666
+ CREATE INDEX idx_workflow_interventions_project_requested
667
+ ON workflow_interventions (project_id, requested_at DESC);
668
+ CREATE INDEX idx_workflow_interventions_project_status_requested
669
+ ON workflow_interventions (project_id, status, requested_at DESC);
670
+ CREATE INDEX idx_workflow_interventions_project_workflow_requested
671
+ ON workflow_interventions (project_id, workflow_id, requested_at DESC);
672
+ CREATE INDEX idx_workflow_interventions_project_run_requested
673
+ ON workflow_interventions (project_id, workflow_run_id, requested_at DESC);
674
+ CREATE INDEX idx_workflow_interventions_project_node_run_requested
675
+ ON workflow_interventions (project_id, node_run_id, requested_at DESC);
676
+ CREATE INDEX idx_workflow_interventions_project_node_requested
677
+ ON workflow_interventions (project_id, node_id, requested_at DESC);
678
+ CREATE INDEX idx_workflow_interventions_project_key_requested
679
+ ON workflow_interventions (project_id, node_key, requested_at DESC);
680
+ CREATE INDEX idx_workflow_interventions_project_intervention_requested
681
+ ON workflow_interventions (project_id, intervention_id, requested_at DESC);
682
+
683
+ CREATE TABLE webhook_deliveries (
684
+ project_id TEXT NOT NULL,
685
+ connector_id TEXT NOT NULL,
686
+ webhook_id TEXT NOT NULL,
687
+ idempotency_key TEXT NOT NULL,
688
+ status TEXT NOT NULL CHECK (status IN ('in_progress', 'completed', 'failed')),
689
+ received_at TIMESTAMPTZ NOT NULL,
690
+ completed_at TIMESTAMPTZ,
691
+ failed_at TIMESTAMPTZ,
692
+ error TEXT,
693
+ PRIMARY KEY (project_id, connector_id, webhook_id, idempotency_key)
694
+ );
695
+
696
+ CREATE INDEX idx_webhook_deliveries_project_received
697
+ ON webhook_deliveries (project_id, received_at DESC);
698
+
699
+ CREATE TABLE webhook_runs (
700
+ project_id TEXT NOT NULL,
701
+ id TEXT NOT NULL,
702
+ connector_id TEXT NOT NULL,
703
+ webhook_id TEXT NOT NULL,
704
+ status TEXT NOT NULL CHECK (status IN ('running', 'succeeded', 'failed', 'skipped')),
705
+ method TEXT NOT NULL,
706
+ route TEXT NOT NULL,
707
+ started_at TIMESTAMPTZ NOT NULL,
708
+ finished_at TIMESTAMPTZ,
709
+ request_body_bytes INTEGER CHECK (request_body_bytes IS NULL OR request_body_bytes >= 0),
710
+ response_status INTEGER CHECK (
711
+ response_status IS NULL OR (response_status >= 100 AND response_status <= 599)
712
+ ),
713
+ idempotency_key TEXT,
714
+ delivery_claim_result TEXT CHECK (
715
+ delivery_claim_result IS NULL OR delivery_claim_result IN ('claimed', 'duplicate', 'in_progress')
716
+ ),
717
+ error TEXT,
718
+ PRIMARY KEY (project_id, id)
719
+ );
720
+
721
+ CREATE INDEX idx_webhook_runs_project_started
722
+ ON webhook_runs (project_id, started_at DESC);
723
+ CREATE INDEX idx_webhook_runs_project_connector_started
724
+ ON webhook_runs (project_id, connector_id, started_at DESC);
725
+ CREATE INDEX idx_webhook_runs_project_webhook_started
726
+ ON webhook_runs (project_id, connector_id, webhook_id, started_at DESC);
727
+ CREATE INDEX idx_webhook_runs_project_status_started
728
+ ON webhook_runs (project_id, status, started_at DESC);
729
+ CREATE INDEX idx_webhook_runs_project_idempotency
730
+ ON webhook_runs (project_id, idempotency_key);
731
+
732
+ CREATE TABLE rule_states (
733
+ project_id TEXT NOT NULL,
734
+ rule_id TEXT NOT NULL,
735
+ subject_kind TEXT NOT NULL CHECK (subject_kind IN ('object')),
736
+ object_type_id TEXT NOT NULL,
737
+ primary_id TEXT NOT NULL,
738
+ triggered_at TIMESTAMPTZ NOT NULL,
739
+ PRIMARY KEY (project_id, rule_id, subject_kind, object_type_id, primary_id)
740
+ );
741
+
742
+ CREATE INDEX idx_rule_states_project_rule
743
+ ON rule_states (project_id, rule_id);
744
+
745
+ CREATE TABLE action_runs (
746
+ project_id TEXT NOT NULL,
747
+ id TEXT NOT NULL,
748
+ action_id TEXT NOT NULL,
749
+ subject_kind TEXT NOT NULL CHECK (subject_kind IN ('none', 'object')),
750
+ object_type_id TEXT,
751
+ primary_id TEXT,
752
+ status TEXT NOT NULL CHECK (status IN ('queued', 'running', 'succeeded', 'failed', 'cancelled')),
753
+ phase TEXT CHECK (
754
+ phase IS NULL OR phase IN (
755
+ 'request',
756
+ 'enqueue',
757
+ 'validation',
758
+ 'writeback',
759
+ 'edits',
760
+ 'commit',
761
+ 'effects',
762
+ 'cancelled'
763
+ )
764
+ ),
765
+ queued_at TIMESTAMPTZ NOT NULL,
766
+ started_at TIMESTAMPTZ,
767
+ finished_at TIMESTAMPTZ,
768
+ params JSONB NOT NULL,
769
+ idempotency_key TEXT NOT NULL,
770
+ writeback_status TEXT CHECK (writeback_status IS NULL OR writeback_status IN ('succeeded', 'failed')),
771
+ writeback_completed_at TIMESTAMPTZ,
772
+ writeback_result JSONB,
773
+ writeback_error_name TEXT,
774
+ writeback_error_message TEXT,
775
+ writeback_error_phase TEXT CHECK (
776
+ writeback_error_phase IS NULL OR writeback_error_phase IN (
777
+ 'request',
778
+ 'enqueue',
779
+ 'validation',
780
+ 'writeback',
781
+ 'edits',
782
+ 'commit',
783
+ 'effects',
784
+ 'cancelled'
785
+ )
786
+ ),
787
+ effects_status TEXT CHECK (effects_status IS NULL OR effects_status IN ('succeeded', 'failed')),
788
+ effects_completed_at TIMESTAMPTZ,
789
+ effects_error_name TEXT,
790
+ effects_error_message TEXT,
791
+ effects_error_phase TEXT CHECK (
792
+ effects_error_phase IS NULL OR effects_error_phase IN (
793
+ 'request',
794
+ 'enqueue',
795
+ 'validation',
796
+ 'writeback',
797
+ 'edits',
798
+ 'commit',
799
+ 'effects',
800
+ 'cancelled'
801
+ )
802
+ ),
803
+ error_name TEXT,
804
+ error_message TEXT,
805
+ error_phase TEXT CHECK (
806
+ error_phase IS NULL OR error_phase IN (
807
+ 'request',
808
+ 'enqueue',
809
+ 'validation',
810
+ 'writeback',
811
+ 'edits',
812
+ 'commit',
813
+ 'effects',
814
+ 'cancelled'
815
+ )
816
+ ),
817
+ CHECK (
818
+ (subject_kind = 'none' AND object_type_id IS NULL AND primary_id IS NULL)
819
+ OR (subject_kind = 'object' AND object_type_id IS NOT NULL AND primary_id IS NOT NULL)
820
+ ),
821
+ PRIMARY KEY (project_id, id)
822
+ );
823
+
824
+ CREATE INDEX idx_action_runs_project_started
825
+ ON action_runs (project_id, (COALESCE(started_at, queued_at)) DESC);
826
+ CREATE INDEX idx_action_runs_project_action_started
827
+ ON action_runs (project_id, action_id, (COALESCE(started_at, queued_at)) DESC);
828
+ CREATE INDEX idx_action_runs_project_object_started
829
+ ON action_runs (project_id, object_type_id, primary_id, (COALESCE(started_at, queued_at)) DESC);
830
+ CREATE INDEX idx_action_runs_project_status_started
831
+ ON action_runs (project_id, status, (COALESCE(started_at, queued_at)) DESC);
832
+
833
+ CREATE TABLE auth_users (
834
+ project_id TEXT NOT NULL,
835
+ id TEXT NOT NULL,
836
+ email TEXT NOT NULL,
837
+ display_name TEXT,
838
+ avatar_url TEXT,
839
+ status TEXT NOT NULL CHECK (status IN ('active', 'suspended')),
840
+ created_at TIMESTAMPTZ NOT NULL,
841
+ updated_at TIMESTAMPTZ NOT NULL,
842
+ PRIMARY KEY (project_id, id)
843
+ );
844
+
845
+ CREATE UNIQUE INDEX idx_auth_users_project_email
846
+ ON auth_users (project_id, email);
847
+ CREATE INDEX idx_auth_users_project_status_created
848
+ ON auth_users (project_id, status, created_at DESC, id DESC);
849
+ CREATE INDEX idx_auth_users_project_created
850
+ ON auth_users (project_id, created_at DESC, id DESC);
851
+
852
+ CREATE TABLE auth_user_identities (
853
+ project_id TEXT NOT NULL,
854
+ strategy_id TEXT NOT NULL,
855
+ subject TEXT NOT NULL,
856
+ user_id TEXT NOT NULL,
857
+ claims JSONB,
858
+ created_at TIMESTAMPTZ NOT NULL,
859
+ updated_at TIMESTAMPTZ NOT NULL,
860
+ PRIMARY KEY (project_id, strategy_id, subject),
861
+ FOREIGN KEY (project_id, user_id)
862
+ REFERENCES auth_users (project_id, id)
863
+ );
864
+
865
+ CREATE INDEX idx_auth_user_identities_user
866
+ ON auth_user_identities (project_id, user_id);
867
+
868
+ CREATE TABLE auth_sessions (
869
+ project_id TEXT NOT NULL,
870
+ id TEXT NOT NULL,
871
+ user_id TEXT NOT NULL,
872
+ strategy_id TEXT NOT NULL,
873
+ audience TEXT NOT NULL DEFAULT 'atlas',
874
+ token_hash TEXT NOT NULL,
875
+ created_at TIMESTAMPTZ NOT NULL,
876
+ expires_at TIMESTAMPTZ NOT NULL,
877
+ absolute_expires_at TIMESTAMPTZ,
878
+ revoked_at TIMESTAMPTZ,
879
+ last_seen_at TIMESTAMPTZ,
880
+ user_agent TEXT,
881
+ ip_address TEXT,
882
+ PRIMARY KEY (project_id, id),
883
+ FOREIGN KEY (project_id, user_id)
884
+ REFERENCES auth_users (project_id, id)
885
+ );
886
+
887
+ CREATE INDEX idx_auth_sessions_user_active
888
+ ON auth_sessions (project_id, user_id, audience, expires_at DESC, created_at DESC, id DESC)
889
+ WHERE revoked_at IS NULL;
890
+ CREATE INDEX idx_auth_sessions_user_created
891
+ ON auth_sessions (project_id, user_id, audience, created_at DESC, id DESC);
892
+
893
+ CREATE TABLE auth_service_accounts (
894
+ project_id TEXT NOT NULL,
895
+ id TEXT NOT NULL,
896
+ name TEXT NOT NULL,
897
+ description TEXT,
898
+ status TEXT NOT NULL CHECK (status IN ('active', 'suspended')),
899
+ created_by_user_id TEXT,
900
+ created_by_service_account_id TEXT,
901
+ created_by_system_id TEXT,
902
+ created_by_session_id TEXT,
903
+ created_at TIMESTAMPTZ NOT NULL,
904
+ updated_at TIMESTAMPTZ NOT NULL,
905
+ PRIMARY KEY (project_id, id),
906
+ CHECK (
907
+ num_nonnulls(created_by_user_id, created_by_service_account_id, created_by_system_id) <= 1
908
+ ),
909
+ FOREIGN KEY (project_id, created_by_user_id)
910
+ REFERENCES auth_users (project_id, id),
911
+ FOREIGN KEY (project_id, created_by_service_account_id)
912
+ REFERENCES auth_service_accounts (project_id, id),
913
+ FOREIGN KEY (project_id, created_by_session_id)
914
+ REFERENCES auth_sessions (project_id, id)
915
+ );
916
+
917
+ CREATE INDEX idx_auth_service_accounts_project_status_created
918
+ ON auth_service_accounts (project_id, status, created_at DESC, id DESC);
919
+ CREATE INDEX idx_auth_service_accounts_project_created
920
+ ON auth_service_accounts (project_id, created_at DESC, id DESC);
921
+ CREATE INDEX idx_auth_service_accounts_created_by_user
922
+ ON auth_service_accounts (project_id, created_by_user_id)
923
+ WHERE created_by_user_id IS NOT NULL;
924
+
925
+ CREATE TABLE auth_service_account_group_memberships (
926
+ project_id TEXT NOT NULL,
927
+ service_account_id TEXT NOT NULL,
928
+ group_id TEXT NOT NULL,
929
+ source TEXT NOT NULL CHECK (source IN ('invitation', 'manual', 'agent')),
930
+ created_at TIMESTAMPTZ NOT NULL,
931
+ PRIMARY KEY (project_id, service_account_id, group_id),
932
+ FOREIGN KEY (project_id, service_account_id)
933
+ REFERENCES auth_service_accounts (project_id, id)
934
+ );
935
+
936
+ CREATE INDEX idx_auth_service_account_group_memberships_group
937
+ ON auth_service_account_group_memberships (project_id, group_id);
938
+
939
+ CREATE TABLE auth_access_tokens (
940
+ project_id TEXT NOT NULL,
941
+ id TEXT NOT NULL,
942
+ name TEXT NOT NULL,
943
+ kind TEXT NOT NULL CHECK (kind IN ('personal', 'serviceAccount')),
944
+ subject_type TEXT NOT NULL CHECK (subject_type IN ('user', 'serviceAccount')),
945
+ subject_id TEXT NOT NULL,
946
+ token_hash TEXT NOT NULL,
947
+ group_ids JSONB,
948
+ created_by_user_id TEXT,
949
+ created_by_service_account_id TEXT,
950
+ created_by_system_id TEXT,
951
+ created_by_session_id TEXT,
952
+ created_at TIMESTAMPTZ NOT NULL,
953
+ expires_at TIMESTAMPTZ NOT NULL,
954
+ revoked_at TIMESTAMPTZ,
955
+ last_used_at TIMESTAMPTZ,
956
+ last_used_user_agent TEXT,
957
+ last_used_ip_address TEXT,
958
+ PRIMARY KEY (project_id, id),
959
+ CHECK (
960
+ (kind = 'personal' AND subject_type = 'user')
961
+ OR (kind = 'serviceAccount' AND subject_type = 'serviceAccount')
962
+ ),
963
+ CHECK (
964
+ num_nonnulls(created_by_user_id, created_by_service_account_id, created_by_system_id) <= 1
965
+ ),
966
+ FOREIGN KEY (project_id, created_by_user_id)
967
+ REFERENCES auth_users (project_id, id),
968
+ FOREIGN KEY (project_id, created_by_service_account_id)
969
+ REFERENCES auth_service_accounts (project_id, id),
970
+ FOREIGN KEY (project_id, created_by_session_id)
971
+ REFERENCES auth_sessions (project_id, id)
972
+ );
973
+
974
+ CREATE INDEX idx_auth_access_tokens_lookup
975
+ ON auth_access_tokens (project_id, id, kind, token_hash, expires_at DESC)
976
+ WHERE revoked_at IS NULL;
977
+ CREATE INDEX idx_auth_access_tokens_subject_created
978
+ ON auth_access_tokens (project_id, subject_type, subject_id, created_at DESC, id DESC);
979
+
980
+ CREATE TABLE auth_invitations (
981
+ project_id TEXT NOT NULL,
982
+ id TEXT NOT NULL,
983
+ email TEXT NOT NULL,
984
+ status TEXT NOT NULL CHECK (status IN ('pending', 'accepted', 'revoked')),
985
+ created_by_user_id TEXT,
986
+ created_by_service_account_id TEXT,
987
+ created_by_system_id TEXT,
988
+ created_by_session_id TEXT,
989
+ created_at TIMESTAMPTZ NOT NULL,
990
+ updated_at TIMESTAMPTZ NOT NULL,
991
+ expires_at TIMESTAMPTZ NOT NULL,
992
+ accepted_at TIMESTAMPTZ,
993
+ revoked_at TIMESTAMPTZ,
994
+ PRIMARY KEY (project_id, id),
995
+ CHECK (
996
+ num_nonnulls(created_by_user_id, created_by_service_account_id, created_by_system_id) <= 1
997
+ ),
998
+ FOREIGN KEY (project_id, created_by_user_id)
999
+ REFERENCES auth_users (project_id, id),
1000
+ FOREIGN KEY (project_id, created_by_session_id)
1001
+ REFERENCES auth_sessions (project_id, id)
1002
+ );
1003
+
1004
+ CREATE INDEX idx_auth_invitations_project_email
1005
+ ON auth_invitations (project_id, email, status, expires_at DESC, created_at DESC, id DESC);
1006
+ CREATE INDEX idx_auth_invitations_project_status_created
1007
+ ON auth_invitations (project_id, status, created_at DESC, id DESC);
1008
+ CREATE INDEX idx_auth_invitations_pending_email
1009
+ ON auth_invitations (project_id, email, expires_at DESC, created_at DESC, id DESC)
1010
+ WHERE status = 'pending';
1011
+ CREATE INDEX idx_auth_invitations_created_by_user
1012
+ ON auth_invitations (project_id, created_by_user_id)
1013
+ WHERE created_by_user_id IS NOT NULL;
1014
+ CREATE INDEX idx_auth_invitations_created_by_session
1015
+ ON auth_invitations (project_id, created_by_session_id)
1016
+ WHERE created_by_session_id IS NOT NULL;
1017
+
1018
+ CREATE TABLE auth_invitation_groups (
1019
+ project_id TEXT NOT NULL,
1020
+ invitation_id TEXT NOT NULL,
1021
+ group_id TEXT NOT NULL,
1022
+ position INTEGER NOT NULL CHECK (position >= 0),
1023
+ PRIMARY KEY (project_id, invitation_id, group_id),
1024
+ FOREIGN KEY (project_id, invitation_id)
1025
+ REFERENCES auth_invitations (project_id, id)
1026
+ );
1027
+
1028
+ CREATE INDEX idx_auth_invitation_groups_group
1029
+ ON auth_invitation_groups (project_id, group_id);
1030
+ CREATE INDEX idx_auth_invitation_groups_invitation_position
1031
+ ON auth_invitation_groups (project_id, invitation_id, position);
1032
+
1033
+ CREATE TABLE auth_group_memberships (
1034
+ project_id TEXT NOT NULL,
1035
+ user_id TEXT NOT NULL,
1036
+ group_id TEXT NOT NULL,
1037
+ source TEXT NOT NULL CHECK (source IN ('invitation', 'manual', 'agent')),
1038
+ created_at TIMESTAMPTZ NOT NULL,
1039
+ PRIMARY KEY (project_id, user_id, group_id),
1040
+ FOREIGN KEY (project_id, user_id)
1041
+ REFERENCES auth_users (project_id, id)
1042
+ );
1043
+
1044
+ CREATE INDEX idx_auth_group_memberships_group
1045
+ ON auth_group_memberships (project_id, group_id);
1046
+
1047
+ CREATE TABLE auth_magic_links (
1048
+ project_id TEXT NOT NULL,
1049
+ id TEXT NOT NULL,
1050
+ strategy_id TEXT NOT NULL,
1051
+ audience TEXT NOT NULL DEFAULT 'atlas',
1052
+ email TEXT NOT NULL,
1053
+ token_hash TEXT NOT NULL,
1054
+ return_to TEXT,
1055
+ created_at TIMESTAMPTZ NOT NULL,
1056
+ expires_at TIMESTAMPTZ NOT NULL,
1057
+ consumed_at TIMESTAMPTZ,
1058
+ revoked_at TIMESTAMPTZ,
1059
+ PRIMARY KEY (project_id, id)
1060
+ );
1061
+
1062
+ CREATE INDEX idx_auth_magic_links_email_active
1063
+ ON auth_magic_links (project_id, email, expires_at DESC, created_at DESC, id DESC)
1064
+ WHERE consumed_at IS NULL AND revoked_at IS NULL;
1065
+
1066
+ CREATE TABLE auth_oidc_authorization_attempts (
1067
+ project_id TEXT NOT NULL,
1068
+ id TEXT NOT NULL,
1069
+ strategy_id TEXT NOT NULL,
1070
+ audience TEXT NOT NULL DEFAULT 'atlas',
1071
+ state_hash TEXT NOT NULL,
1072
+ nonce_hash TEXT NOT NULL,
1073
+ code_verifier TEXT NOT NULL,
1074
+ return_to TEXT,
1075
+ created_at TIMESTAMPTZ NOT NULL,
1076
+ expires_at TIMESTAMPTZ NOT NULL,
1077
+ consumed_at TIMESTAMPTZ,
1078
+ PRIMARY KEY (project_id, id)
1079
+ );
1080
+
1081
+ CREATE INDEX idx_auth_oidc_attempts_active
1082
+ ON auth_oidc_authorization_attempts (
1083
+ project_id,
1084
+ strategy_id,
1085
+ expires_at DESC,
1086
+ created_at DESC,
1087
+ id DESC
1088
+ )
1089
+ WHERE consumed_at IS NULL;
1090
+
1091
+ CREATE TABLE agent_threads (
1092
+ project_id TEXT NOT NULL,
1093
+ id TEXT NOT NULL,
1094
+ agent_id TEXT NOT NULL,
1095
+ owner_principal_type TEXT NOT NULL,
1096
+ owner_principal_id TEXT NOT NULL,
1097
+ title TEXT,
1098
+ status TEXT NOT NULL CHECK (status IN ('active', 'archived')),
1099
+ active_run_id TEXT,
1100
+ last_message_at TIMESTAMPTZ,
1101
+ message_count INTEGER NOT NULL DEFAULT 0 CHECK (message_count >= 0),
1102
+ created_at TIMESTAMPTZ NOT NULL,
1103
+ updated_at TIMESTAMPTZ NOT NULL,
1104
+ PRIMARY KEY (project_id, id)
1105
+ );
1106
+
1107
+ CREATE INDEX idx_agent_threads_project_activity
1108
+ ON agent_threads (project_id, (COALESCE(last_message_at, created_at)) DESC, id DESC);
1109
+ CREATE INDEX idx_agent_threads_project_agent_activity
1110
+ ON agent_threads (project_id, agent_id, (COALESCE(last_message_at, created_at)) DESC, id DESC);
1111
+ CREATE INDEX idx_agent_threads_project_status_activity
1112
+ ON agent_threads (project_id, status, (COALESCE(last_message_at, created_at)) DESC, id DESC);
1113
+ CREATE INDEX idx_agent_threads_project_owner_activity
1114
+ ON agent_threads (
1115
+ project_id,
1116
+ owner_principal_type,
1117
+ owner_principal_id,
1118
+ (COALESCE(last_message_at, created_at)) DESC,
1119
+ id DESC
1120
+ );
1121
+
1122
+ CREATE TABLE agent_runs (
1123
+ project_id TEXT NOT NULL,
1124
+ id TEXT NOT NULL,
1125
+ thread_id TEXT NOT NULL,
1126
+ agent_id TEXT NOT NULL,
1127
+ trigger_message_id TEXT NOT NULL,
1128
+ requested_by_principal_type TEXT NOT NULL DEFAULT 'system' CHECK (
1129
+ requested_by_principal_type IN ('user', 'serviceAccount', 'system')
1130
+ ),
1131
+ requested_by_principal_id TEXT NOT NULL DEFAULT 'system',
1132
+ execution_principal_type TEXT CHECK (
1133
+ execution_principal_type IS NULL OR execution_principal_type = 'serviceAccount'
1134
+ ),
1135
+ execution_principal_id TEXT,
1136
+ status TEXT NOT NULL CHECK (
1137
+ status IN ('queued', 'running', 'succeeded', 'failed', 'cancelled')
1138
+ ),
1139
+ model_id TEXT,
1140
+ finish_reason TEXT,
1141
+ usage_input_tokens INTEGER CHECK (usage_input_tokens IS NULL OR usage_input_tokens >= 0),
1142
+ usage_output_tokens INTEGER CHECK (usage_output_tokens IS NULL OR usage_output_tokens >= 0),
1143
+ usage_total_tokens INTEGER CHECK (usage_total_tokens IS NULL OR usage_total_tokens >= 0),
1144
+ usage_reasoning_tokens INTEGER CHECK (
1145
+ usage_reasoning_tokens IS NULL OR usage_reasoning_tokens >= 0
1146
+ ),
1147
+ usage_cached_input_tokens INTEGER CHECK (
1148
+ usage_cached_input_tokens IS NULL OR usage_cached_input_tokens >= 0
1149
+ ),
1150
+ error TEXT,
1151
+ diagnostics JSONB,
1152
+ attempt INTEGER NOT NULL DEFAULT 0 CHECK (attempt >= 0),
1153
+ execution_token TEXT,
1154
+ execution_queue_lease_expires_at TIMESTAMPTZ,
1155
+ created_at TIMESTAMPTZ NOT NULL,
1156
+ started_at TIMESTAMPTZ,
1157
+ completed_at TIMESTAMPTZ,
1158
+ PRIMARY KEY (project_id, id),
1159
+ CHECK (
1160
+ (execution_token IS NULL) = (execution_queue_lease_expires_at IS NULL)
1161
+ )
1162
+ );
1163
+
1164
+ CREATE INDEX idx_agent_runs_project_started
1165
+ ON agent_runs (project_id, COALESCE(started_at, created_at) DESC, id DESC);
1166
+ CREATE INDEX idx_agent_runs_project_thread_started
1167
+ ON agent_runs (project_id, thread_id, COALESCE(started_at, created_at) DESC, id DESC);
1168
+ CREATE INDEX idx_agent_runs_project_agent_started
1169
+ ON agent_runs (project_id, agent_id, COALESCE(started_at, created_at) DESC, id DESC);
1170
+ CREATE INDEX idx_agent_runs_project_status_started
1171
+ ON agent_runs (project_id, status, COALESCE(started_at, created_at) DESC, id DESC);
1172
+
1173
+ CREATE TABLE agent_messages (
1174
+ project_id TEXT NOT NULL,
1175
+ id TEXT NOT NULL,
1176
+ thread_id TEXT NOT NULL,
1177
+ run_id TEXT,
1178
+ role TEXT NOT NULL CHECK (role IN ('system', 'user', 'assistant')),
1179
+ author_principal_type TEXT CHECK (
1180
+ author_principal_type IS NULL OR author_principal_type IN ('user', 'serviceAccount', 'system')
1181
+ ),
1182
+ author_principal_id TEXT,
1183
+ seq INTEGER NOT NULL CHECK (seq >= 1),
1184
+ parts JSONB NOT NULL,
1185
+ metadata JSONB,
1186
+ content_version INTEGER NOT NULL CHECK (content_version >= 1),
1187
+ created_at TIMESTAMPTZ NOT NULL,
1188
+ completed_at TIMESTAMPTZ,
1189
+ PRIMARY KEY (project_id, id)
1190
+ );
1191
+
1192
+ CREATE UNIQUE INDEX idx_agent_messages_thread_seq
1193
+ ON agent_messages (project_id, thread_id, seq);
1194
+ CREATE INDEX idx_agent_messages_project_thread_role
1195
+ ON agent_messages (project_id, thread_id, role, seq);