@opengeni/db 0.31.1 → 0.36.0

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 (122) hide show
  1. package/dist/browser-auth.d.ts +209 -1
  2. package/dist/browser-downloads.d.ts +51 -0
  3. package/dist/browser-identities.d.ts +11 -2
  4. package/dist/browser-sessions.d.ts +83 -1
  5. package/dist/browser-state-artifacts.d.ts +75 -1
  6. package/dist/canonical-human-identities.d.ts +48 -0
  7. package/dist/canonical-human-identities.js +28 -0
  8. package/dist/canonical-human-identities.js.map +1 -0
  9. package/dist/capability-integrations.d.ts +8 -6
  10. package/dist/{chunk-MWDVXQOL.js → chunk-5KTIEDX7.js} +5773 -4212
  11. package/dist/chunk-5KTIEDX7.js.map +1 -0
  12. package/dist/{chunk-NBE6CHRI.js → chunk-CQZ4QSP5.js} +49 -9
  13. package/dist/chunk-CQZ4QSP5.js.map +1 -0
  14. package/dist/chunk-F32YT7MP.js +117 -0
  15. package/dist/chunk-F32YT7MP.js.map +1 -0
  16. package/dist/{chunk-ISI2TIUG.js → chunk-JC5QPLUM.js} +30 -3
  17. package/dist/chunk-JC5QPLUM.js.map +1 -0
  18. package/dist/{chunk-F5FT7BEZ.js → chunk-P3RE4D2B.js} +2 -2
  19. package/dist/chunk-TAUTWOS5.js +259 -0
  20. package/dist/chunk-TAUTWOS5.js.map +1 -0
  21. package/dist/{chunk-YHZXQ7HP.js → chunk-VKLB2ZGI.js} +446 -19
  22. package/dist/chunk-VKLB2ZGI.js.map +1 -0
  23. package/dist/database.d.ts +5 -0
  24. package/dist/editable-artifact-durable-export.js +2 -2
  25. package/dist/editable-artifacts.js +3 -3
  26. package/dist/index.d.ts +438 -29
  27. package/dist/index.js +35640 -28969
  28. package/dist/index.js.map +1 -1
  29. package/dist/insights.d.ts +23 -7
  30. package/dist/integration-bindings.d.ts +24 -24
  31. package/dist/integration-facets.d.ts +99 -0
  32. package/dist/interaction-revisions.d.ts +11 -0
  33. package/dist/interaction-schema.d.ts +698 -34
  34. package/dist/provision-roles.js +1 -1
  35. package/dist/runtime-posture.d.ts +8 -7
  36. package/dist/schema.d.ts +9464 -5412
  37. package/dist/schema.js +51 -9
  38. package/dist/session-control.d.ts +29 -3
  39. package/dist/session-tenancy.d.ts +44 -0
  40. package/dist/session-tenancy.js +16 -0
  41. package/dist/session-tenancy.js.map +1 -0
  42. package/dist/slack-task-policy-schema.d.ts +847 -0
  43. package/dist/slack-task-policy.d.ts +65 -0
  44. package/dist/task-notes-schema.d.ts +924 -0
  45. package/dist/task-notes.d.ts +85 -0
  46. package/dist/video-generation.d.ts +9 -0
  47. package/dist/video-generation.js +5 -3
  48. package/dist/xai-subscription.d.ts +326 -0
  49. package/drizzle/0212_browser_state_transfer_hardening.sql +560 -0
  50. package/drizzle/0213_browser_interaction_authority.sql +67 -0
  51. package/drizzle/0214_browser_download_saves.sql +34 -0
  52. package/drizzle/0215_browser_controller_host.sql +385 -0
  53. package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
  54. package/drizzle/0217_external_browser_auth_operations.sql +22 -0
  55. package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
  56. package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
  57. package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
  58. package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
  59. package/drizzle/0220_session_channels.sql +54 -0
  60. package/drizzle/0221_sessions_channel_index.sql +5 -0
  61. package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
  62. package/drizzle/0222_sessions_channel_fk.sql +89 -0
  63. package/drizzle/0223_pending_tool_event_output.sql +55 -0
  64. package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
  65. package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
  66. package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
  67. package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
  68. package/drizzle/0227_slack_native_actions.sql +145 -0
  69. package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
  70. package/drizzle/0228_slack_task_policy.sql +400 -0
  71. package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
  72. package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
  73. package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
  74. package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
  75. package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
  76. package/drizzle/0234_xai_subscription_authority.sql +1139 -0
  77. package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
  78. package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
  79. package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
  80. package/drizzle/0237_interaction_transition_reaper.sql +263 -0
  81. package/drizzle/0238_goal_persistence_policy.sql +471 -0
  82. package/drizzle/0238_supergrok_realtime_model.sql +29 -0
  83. package/drizzle/0239_supergrok_video_funding.sql +50 -0
  84. package/drizzle/0239_task_tree_notes.sql +990 -0
  85. package/package.json +13 -5
  86. package/src/browser-auth.ts +2600 -123
  87. package/src/browser-downloads.ts +517 -0
  88. package/src/browser-identities.ts +220 -3
  89. package/src/browser-sessions.ts +334 -45
  90. package/src/browser-state-artifacts.ts +402 -12
  91. package/src/canonical-human-identities.ts +317 -0
  92. package/src/capability-integrations.ts +121 -213
  93. package/src/database.ts +41 -1
  94. package/src/index.ts +4157 -394
  95. package/src/insights.ts +109 -28
  96. package/src/integration-bindings.ts +101 -101
  97. package/src/{integration-features.ts → integration-facets.ts} +187 -184
  98. package/src/interaction-revisions.ts +33 -3
  99. package/src/interaction-schema.ts +311 -8
  100. package/src/pack-components.ts +43 -43
  101. package/src/pack-installations.ts +0 -43
  102. package/src/plugin-packages.ts +9 -9
  103. package/src/provision-roles.ts +232 -0
  104. package/src/runtime-posture.ts +227 -14
  105. package/src/schema.ts +1188 -75
  106. package/src/session-control.ts +122 -37
  107. package/src/session-queue-commands.ts +78 -1
  108. package/src/session-tenancy.ts +188 -0
  109. package/src/session-tool-call-settlement.ts +9 -3
  110. package/src/slack-task-policy-schema.ts +130 -0
  111. package/src/slack-task-policy.ts +341 -0
  112. package/src/task-notes-schema.ts +135 -0
  113. package/src/task-notes.ts +178 -0
  114. package/src/video-generation.ts +90 -12
  115. package/src/workspace-artifacts.ts +1 -1
  116. package/src/xai-subscription.ts +1887 -0
  117. package/dist/chunk-ISI2TIUG.js.map +0 -1
  118. package/dist/chunk-MWDVXQOL.js.map +0 -1
  119. package/dist/chunk-NBE6CHRI.js.map +0 -1
  120. package/dist/chunk-YHZXQ7HP.js.map +0 -1
  121. package/dist/integration-features.d.ts +0 -99
  122. /package/dist/{chunk-F5FT7BEZ.js.map → chunk-P3RE4D2B.js.map} +0 -0
@@ -0,0 +1,560 @@
1
+ -- deployment-mode: maintenance
2
+ -- Upgrade the already-shipped browser identity schema without rewriting its
3
+ -- migration history. Lifecycle constraints and the immutable guard are replaced
4
+ -- under one bounded maintenance transaction before upload authority is exposed.
5
+ SET LOCAL lock_timeout = '5s';
6
+ SET LOCAL statement_timeout = '30s';
7
+
8
+ DROP TRIGGER "browser_state_artifacts_update_guard_trg"
9
+ ON "browser_state_artifacts";
10
+
11
+ ALTER TABLE "browser_state_artifacts"
12
+ ALTER COLUMN "encrypted_data_key" DROP NOT NULL,
13
+ ADD COLUMN "delete_claim_id" uuid,
14
+ ADD COLUMN "delete_claimed_at" timestamptz;
15
+
16
+ UPDATE "browser_state_artifacts"
17
+ SET "retained_until" = coalesce("retained_until", "deleted_at", now()),
18
+ "encrypted_data_key" = CASE
19
+ WHEN "state" = 'deleted' THEN NULL
20
+ ELSE "encrypted_data_key"
21
+ END
22
+ WHERE "state" IN ('delete_pending', 'deleted');
23
+
24
+ ALTER TABLE "browser_state_artifacts"
25
+ DROP CONSTRAINT "browser_state_artifacts_state_check",
26
+ DROP CONSTRAINT "browser_state_artifacts_values_check",
27
+ DROP CONSTRAINT "browser_state_artifacts_lifecycle_check",
28
+ ADD CONSTRAINT "browser_state_artifacts_state_check"
29
+ CHECK ("state" IN ('available', 'delete_pending', 'deleting', 'deleted')),
30
+ ADD CONSTRAINT "browser_state_artifacts_values_check" CHECK (
31
+ octet_length("format") BETWEEN 1 AND 512
32
+ AND "format" = btrim("format")
33
+ AND "artifact_digest" ~ '^[0-9a-f]{64}$'
34
+ AND "content_digest" ~ '^[0-9a-f]{64}$'
35
+ AND "manifest_digest" ~ '^[0-9a-f]{64}$'
36
+ AND octet_length("object_key") BETWEEN 1 AND 2048
37
+ AND "object_key" ~ (
38
+ '^workspaces/' || "workspace_id"::text ||
39
+ '/browser-state/[A-Za-z0-9._=-]+(/[A-Za-z0-9._=-]+)*$'
40
+ )
41
+ AND (
42
+ "encrypted_data_key" IS NULL
43
+ OR octet_length("encrypted_data_key") BETWEEN 16 AND 8192
44
+ )
45
+ AND "size_bytes" > 0
46
+ AND jsonb_typeof("materialization") = 'object'
47
+ AND octet_length("materialization"::text) BETWEEN 2 AND 65536
48
+ AND (
49
+ "purpose" <> 'revision_component'
50
+ OR (
51
+ "state" = 'available'
52
+ AND "retained_until" IS NULL
53
+ AND "delete_claim_id" IS NULL
54
+ AND "delete_claimed_at" IS NULL
55
+ AND "deleted_at" IS NULL
56
+ AND "encrypted_data_key" IS NOT NULL
57
+ )
58
+ )
59
+ ),
60
+ ADD CONSTRAINT "browser_state_artifacts_lifecycle_check" CHECK (
61
+ (
62
+ "state" = 'available'
63
+ AND "retained_until" IS NULL
64
+ AND "delete_claim_id" IS NULL
65
+ AND "delete_claimed_at" IS NULL
66
+ AND "deleted_at" IS NULL
67
+ AND "encrypted_data_key" IS NOT NULL
68
+ ) OR (
69
+ "state" = 'delete_pending'
70
+ AND "retained_until" IS NOT NULL
71
+ AND "delete_claim_id" IS NULL
72
+ AND "delete_claimed_at" IS NULL
73
+ AND "deleted_at" IS NULL
74
+ AND "encrypted_data_key" IS NOT NULL
75
+ ) OR (
76
+ "state" = 'deleting'
77
+ AND "retained_until" IS NOT NULL
78
+ AND "delete_claim_id" IS NOT NULL
79
+ AND "delete_claimed_at" IS NOT NULL
80
+ AND "deleted_at" IS NULL
81
+ AND "encrypted_data_key" IS NOT NULL
82
+ ) OR (
83
+ "state" = 'deleted'
84
+ AND "retained_until" IS NOT NULL
85
+ AND "delete_claim_id" IS NULL
86
+ AND "delete_claimed_at" IS NULL
87
+ AND "deleted_at" IS NOT NULL
88
+ AND "encrypted_data_key" IS NULL
89
+ )
90
+ ),
91
+ ADD CONSTRAINT "browser_state_artifacts_commit_authority_uq"
92
+ UNIQUE ("workspace_id", "id", "purpose", "source_browser_session_id");
93
+
94
+ DROP INDEX "browser_state_artifacts_gc_idx";
95
+ CREATE INDEX "browser_state_artifacts_gc_idx"
96
+ ON "browser_state_artifacts" (
97
+ "state", "retained_until", "delete_claimed_at", "created_at"
98
+ );
99
+
100
+ CREATE TABLE "browser_state_uploads" (
101
+ "id" uuid PRIMARY KEY,
102
+ "account_id" uuid NOT NULL,
103
+ "workspace_id" uuid NOT NULL,
104
+ "operation_id" uuid NOT NULL,
105
+ "source_browser_session_id" uuid NOT NULL,
106
+ "purpose" text NOT NULL,
107
+ "object_key" text NOT NULL,
108
+ "state" text NOT NULL DEFAULT 'prepared',
109
+ "cleanup_after" timestamptz,
110
+ "committed_artifact_id" uuid,
111
+ "delete_claim_id" uuid,
112
+ "delete_claimed_at" timestamptz,
113
+ "created_at" timestamptz NOT NULL DEFAULT now(),
114
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
115
+ "deleted_at" timestamptz,
116
+ CONSTRAINT "browser_state_uploads_workspace_account_fk"
117
+ FOREIGN KEY ("workspace_id", "account_id")
118
+ REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE,
119
+ CONSTRAINT "browser_state_uploads_operation_fk"
120
+ FOREIGN KEY ("workspace_id", "operation_id")
121
+ REFERENCES "interaction_operations"("workspace_id", "operation_id") ON DELETE RESTRICT,
122
+ CONSTRAINT "browser_state_uploads_source_session_fk"
123
+ FOREIGN KEY ("workspace_id", "source_browser_session_id")
124
+ REFERENCES "browser_sessions"("workspace_id", "id") ON DELETE RESTRICT,
125
+ CONSTRAINT "browser_state_uploads_committed_artifact_fk"
126
+ FOREIGN KEY (
127
+ "workspace_id", "committed_artifact_id", "purpose", "source_browser_session_id"
128
+ ) REFERENCES "browser_state_artifacts"(
129
+ "workspace_id", "id", "purpose", "source_browser_session_id"
130
+ ) ON DELETE RESTRICT,
131
+ CONSTRAINT "browser_state_uploads_purpose_check"
132
+ CHECK ("purpose" IN ('revision_component', 'private_checkpoint')),
133
+ CONSTRAINT "browser_state_uploads_state_check"
134
+ CHECK ("state" IN ('prepared', 'delete_pending', 'deleting', 'committed', 'deleted')),
135
+ CONSTRAINT "browser_state_uploads_values_check" CHECK (
136
+ octet_length("object_key") BETWEEN 1 AND 2048
137
+ AND "object_key" ~ (
138
+ '^workspaces/' || "workspace_id"::text ||
139
+ '/browser-state/[A-Za-z0-9._=-]+(/[A-Za-z0-9._=-]+)*$'
140
+ )
141
+ AND "updated_at" >= "created_at"
142
+ ),
143
+ CONSTRAINT "browser_state_uploads_lifecycle_check" CHECK (
144
+ (
145
+ "state" = 'prepared'
146
+ AND "cleanup_after" IS NOT NULL
147
+ AND "committed_artifact_id" IS NULL
148
+ AND "delete_claim_id" IS NULL
149
+ AND "delete_claimed_at" IS NULL
150
+ AND "deleted_at" IS NULL
151
+ ) OR (
152
+ "state" = 'delete_pending'
153
+ AND "cleanup_after" IS NOT NULL
154
+ AND "committed_artifact_id" IS NULL
155
+ AND "delete_claim_id" IS NULL
156
+ AND "delete_claimed_at" IS NULL
157
+ AND "deleted_at" IS NULL
158
+ ) OR (
159
+ "state" = 'deleting'
160
+ AND "cleanup_after" IS NOT NULL
161
+ AND "committed_artifact_id" IS NULL
162
+ AND "delete_claim_id" IS NOT NULL
163
+ AND "delete_claimed_at" IS NOT NULL
164
+ AND "deleted_at" IS NULL
165
+ ) OR (
166
+ "state" = 'committed'
167
+ AND "cleanup_after" IS NULL
168
+ AND "committed_artifact_id" IS NOT NULL
169
+ AND "delete_claim_id" IS NULL
170
+ AND "delete_claimed_at" IS NULL
171
+ AND "deleted_at" IS NULL
172
+ ) OR (
173
+ "state" = 'deleted'
174
+ AND "cleanup_after" IS NOT NULL
175
+ AND "committed_artifact_id" IS NULL
176
+ AND "delete_claim_id" IS NULL
177
+ AND "delete_claimed_at" IS NULL
178
+ AND "deleted_at" IS NOT NULL
179
+ )
180
+ ),
181
+ CONSTRAINT "browser_state_uploads_workspace_id_uq" UNIQUE ("workspace_id", "id"),
182
+ CONSTRAINT "browser_state_uploads_operation_object_uq"
183
+ UNIQUE ("workspace_id", "operation_id", "object_key"),
184
+ CONSTRAINT "browser_state_uploads_object_key_uq" UNIQUE ("object_key")
185
+ );
186
+
187
+ CREATE INDEX "browser_state_uploads_gc_idx"
188
+ ON "browser_state_uploads" (
189
+ "state", "cleanup_after", "delete_claimed_at", "created_at"
190
+ );
191
+
192
+ CREATE OR REPLACE FUNCTION opengeni_private.browser_state_artifacts_update_guard()
193
+ RETURNS trigger
194
+ LANGUAGE plpgsql
195
+ SET search_path = pg_catalog
196
+ AS $guard$
197
+ BEGIN
198
+ IF ROW(
199
+ NEW.id, NEW.account_id, NEW.workspace_id, NEW.source_browser_session_id,
200
+ NEW.purpose, NEW.kind, NEW.format, NEW.artifact_digest, NEW.content_digest,
201
+ NEW.manifest_digest, NEW.object_key, NEW.size_bytes, NEW.materialization,
202
+ NEW.created_at
203
+ ) IS DISTINCT FROM ROW(
204
+ OLD.id, OLD.account_id, OLD.workspace_id, OLD.source_browser_session_id,
205
+ OLD.purpose, OLD.kind, OLD.format, OLD.artifact_digest, OLD.content_digest,
206
+ OLD.manifest_digest, OLD.object_key, OLD.size_bytes, OLD.materialization,
207
+ OLD.created_at
208
+ ) THEN
209
+ RAISE EXCEPTION 'Browser state artifact immutable authority cannot change'
210
+ USING ERRCODE = '23514';
211
+ END IF;
212
+ IF OLD.purpose = 'revision_component'
213
+ AND ROW(
214
+ NEW.state, NEW.retained_until, NEW.delete_claim_id, NEW.delete_claimed_at,
215
+ NEW.deleted_at, NEW.encrypted_data_key
216
+ ) IS DISTINCT FROM ROW(
217
+ OLD.state, OLD.retained_until, OLD.delete_claim_id, OLD.delete_claimed_at,
218
+ OLD.deleted_at, OLD.encrypted_data_key
219
+ ) THEN
220
+ RAISE EXCEPTION 'Published BrowserRevision artifact immutable authority cannot change'
221
+ USING ERRCODE = '23514';
222
+ END IF;
223
+ IF NEW.state IS DISTINCT FROM OLD.state
224
+ AND NOT (
225
+ (OLD.state = 'available' AND NEW.state = 'delete_pending')
226
+ OR (OLD.state = 'delete_pending' AND NEW.state = 'deleting')
227
+ OR (OLD.state = 'deleting' AND NEW.state = 'deleted')
228
+ ) THEN
229
+ RAISE EXCEPTION 'Browser state artifact lifecycle cannot move backwards or skip a phase'
230
+ USING ERRCODE = '23514';
231
+ END IF;
232
+ IF NEW.state = OLD.state AND NEW.state <> 'deleting'
233
+ AND ROW(
234
+ NEW.retained_until, NEW.delete_claim_id, NEW.delete_claimed_at,
235
+ NEW.deleted_at, NEW.encrypted_data_key
236
+ ) IS DISTINCT FROM ROW(
237
+ OLD.retained_until, OLD.delete_claim_id, OLD.delete_claimed_at,
238
+ OLD.deleted_at, OLD.encrypted_data_key
239
+ ) THEN
240
+ RAISE EXCEPTION 'Browser state artifact lifecycle authority cannot change in place'
241
+ USING ERRCODE = '23514';
242
+ END IF;
243
+ IF NEW.state = OLD.state AND NEW.state = 'deleting'
244
+ AND (
245
+ NEW.retained_until IS DISTINCT FROM OLD.retained_until
246
+ OR NEW.deleted_at IS DISTINCT FROM OLD.deleted_at
247
+ OR NEW.encrypted_data_key IS DISTINCT FROM OLD.encrypted_data_key
248
+ OR NEW.delete_claimed_at < OLD.delete_claimed_at
249
+ ) THEN
250
+ RAISE EXCEPTION 'Browser state artifact delete claim cannot weaken authority'
251
+ USING ERRCODE = '23514';
252
+ END IF;
253
+ RETURN NEW;
254
+ END
255
+ $guard$;
256
+
257
+ CREATE TRIGGER "browser_state_artifacts_update_guard_trg"
258
+ BEFORE UPDATE ON "browser_state_artifacts"
259
+ FOR EACH ROW EXECUTE FUNCTION opengeni_private.browser_state_artifacts_update_guard();
260
+
261
+ CREATE FUNCTION opengeni_private.browser_state_uploads_update_guard()
262
+ RETURNS trigger
263
+ LANGUAGE plpgsql
264
+ SET search_path = pg_catalog
265
+ AS $guard$
266
+ BEGIN
267
+ IF ROW(
268
+ NEW.id, NEW.account_id, NEW.workspace_id, NEW.operation_id,
269
+ NEW.source_browser_session_id, NEW.purpose, NEW.object_key, NEW.created_at
270
+ ) IS DISTINCT FROM ROW(
271
+ OLD.id, OLD.account_id, OLD.workspace_id, OLD.operation_id,
272
+ OLD.source_browser_session_id, OLD.purpose, OLD.object_key, OLD.created_at
273
+ ) THEN
274
+ RAISE EXCEPTION 'Browser state upload immutable authority cannot change'
275
+ USING ERRCODE = '23514';
276
+ END IF;
277
+ IF NEW.state IS DISTINCT FROM OLD.state
278
+ AND NOT (
279
+ (OLD.state = 'prepared' AND NEW.state IN ('delete_pending', 'deleting', 'committed'))
280
+ OR (OLD.state = 'delete_pending' AND NEW.state = 'deleting')
281
+ OR (OLD.state = 'deleting' AND NEW.state = 'deleted')
282
+ ) THEN
283
+ RAISE EXCEPTION 'Browser state upload lifecycle cannot move backwards or skip authority'
284
+ USING ERRCODE = '23514';
285
+ END IF;
286
+ IF NEW.state = OLD.state AND NEW.state = 'prepared'
287
+ AND (
288
+ NEW.cleanup_after < OLD.cleanup_after
289
+ OR NEW.committed_artifact_id IS DISTINCT FROM OLD.committed_artifact_id
290
+ OR NEW.delete_claim_id IS DISTINCT FROM OLD.delete_claim_id
291
+ OR NEW.delete_claimed_at IS DISTINCT FROM OLD.delete_claimed_at
292
+ OR NEW.deleted_at IS DISTINCT FROM OLD.deleted_at
293
+ ) THEN
294
+ RAISE EXCEPTION 'Browser state upload lease cannot weaken authority'
295
+ USING ERRCODE = '23514';
296
+ END IF;
297
+ IF NEW.state = OLD.state AND NEW.state NOT IN ('prepared', 'deleting')
298
+ AND ROW(
299
+ NEW.cleanup_after, NEW.committed_artifact_id, NEW.delete_claim_id,
300
+ NEW.delete_claimed_at, NEW.deleted_at
301
+ ) IS DISTINCT FROM ROW(
302
+ OLD.cleanup_after, OLD.committed_artifact_id, OLD.delete_claim_id,
303
+ OLD.delete_claimed_at, OLD.deleted_at
304
+ ) THEN
305
+ RAISE EXCEPTION 'Browser state upload lifecycle authority cannot change in place'
306
+ USING ERRCODE = '23514';
307
+ END IF;
308
+ IF NEW.state = OLD.state AND NEW.state = 'deleting'
309
+ AND (
310
+ NEW.cleanup_after IS DISTINCT FROM OLD.cleanup_after
311
+ OR NEW.committed_artifact_id IS DISTINCT FROM OLD.committed_artifact_id
312
+ OR NEW.deleted_at IS DISTINCT FROM OLD.deleted_at
313
+ OR NEW.delete_claimed_at < OLD.delete_claimed_at
314
+ ) THEN
315
+ RAISE EXCEPTION 'Browser state upload delete claim cannot weaken authority'
316
+ USING ERRCODE = '23514';
317
+ END IF;
318
+ IF NEW.updated_at < OLD.updated_at THEN
319
+ RAISE EXCEPTION 'Browser state upload time cannot move backwards'
320
+ USING ERRCODE = '23514';
321
+ END IF;
322
+ RETURN NEW;
323
+ END
324
+ $guard$;
325
+
326
+ CREATE TRIGGER "browser_state_uploads_update_guard_trg"
327
+ BEFORE UPDATE ON "browser_state_uploads"
328
+ FOR EACH ROW EXECUTE FUNCTION opengeni_private.browser_state_uploads_update_guard();
329
+
330
+ REVOKE ALL ON FUNCTION opengeni_private.browser_identities_update_guard() FROM PUBLIC;
331
+ REVOKE ALL ON FUNCTION opengeni_private.browser_state_artifacts_update_guard() FROM PUBLIC;
332
+ REVOKE ALL ON FUNCTION opengeni_private.browser_state_uploads_update_guard() FROM PUBLIC;
333
+
334
+ ALTER TABLE "browser_state_uploads" ENABLE ROW LEVEL SECURITY;
335
+ ALTER TABLE "browser_state_uploads" FORCE ROW LEVEL SECURITY;
336
+ CREATE POLICY workspace_isolation ON "browser_state_uploads"
337
+ USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
338
+ WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
339
+
340
+ -- This is the sole bounded FORCE-RLS bypass for browser artifact collection.
341
+ -- Claims are reclaimable after worker loss; provider deletes are idempotent.
342
+ DO $migration$
343
+ DECLARE target_schema text := current_schema();
344
+ BEGIN
345
+ EXECUTE format($create$
346
+ CREATE OR REPLACE FUNCTION opengeni_private.claim_browser_state_artifact_cleanup(
347
+ p_claim_timeout_ms bigint,
348
+ p_limit integer
349
+ )
350
+ RETURNS TABLE (
351
+ claim_id uuid,
352
+ artifact_id uuid,
353
+ account_id uuid,
354
+ workspace_id uuid,
355
+ object_key text
356
+ )
357
+ LANGUAGE sql
358
+ SECURITY DEFINER
359
+ SET search_path = pg_catalog
360
+ AS $function$
361
+ WITH candidates AS (
362
+ SELECT A.id, gen_random_uuid() AS claim_id
363
+ FROM %1$I.browser_state_artifacts A
364
+ WHERE A.purpose = 'private_checkpoint'
365
+ AND (
366
+ (
367
+ A.state = 'delete_pending'
368
+ AND A.retained_until <= clock_timestamp()
369
+ )
370
+ OR (
371
+ A.state = 'deleting'
372
+ AND A.delete_claimed_at <= clock_timestamp() - (
373
+ greatest(p_claim_timeout_ms, 0)::double precision * interval '1 millisecond'
374
+ )
375
+ )
376
+ )
377
+ ORDER BY coalesce(A.delete_claimed_at, A.retained_until), A.id
378
+ LIMIT least(greatest(p_limit, 0), 1000)
379
+ FOR UPDATE SKIP LOCKED
380
+ ), claimed AS (
381
+ UPDATE %1$I.browser_state_artifacts A
382
+ SET state = 'deleting',
383
+ delete_claim_id = C.claim_id,
384
+ delete_claimed_at = clock_timestamp()
385
+ FROM candidates C
386
+ WHERE A.id = C.id
387
+ RETURNING A.*, C.claim_id
388
+ )
389
+ SELECT C.claim_id, C.id, C.account_id, C.workspace_id, C.object_key
390
+ FROM claimed C;
391
+ $function$;
392
+ $create$, target_schema);
393
+ END $migration$;
394
+
395
+ REVOKE ALL ON FUNCTION opengeni_private.claim_browser_state_artifact_cleanup(bigint, integer)
396
+ FROM PUBLIC;
397
+
398
+ -- Fence orphan direct uploads before deleting their exact object. Expired live
399
+ -- operations fail before dispatch or become outcome-unknown after it in the
400
+ -- same transaction; suspension returns active only pre-dispatch, else lost.
401
+ DO $migration$
402
+ DECLARE target_schema text := current_schema();
403
+ BEGIN
404
+ EXECUTE format($create$
405
+ CREATE OR REPLACE FUNCTION opengeni_private.claim_browser_state_upload_cleanup(
406
+ p_claim_timeout_ms bigint,
407
+ p_limit integer
408
+ )
409
+ RETURNS TABLE (
410
+ claim_id uuid,
411
+ upload_id uuid,
412
+ account_id uuid,
413
+ workspace_id uuid,
414
+ object_key text
415
+ )
416
+ LANGUAGE sql
417
+ SECURITY DEFINER
418
+ SET search_path = pg_catalog
419
+ AS $function$
420
+ WITH due AS MATERIALIZED (
421
+ SELECT U.id, U.workspace_id, U.operation_id
422
+ FROM %1$I.browser_state_uploads U
423
+ WHERE (
424
+ U.state IN ('prepared', 'delete_pending')
425
+ AND U.cleanup_after <= clock_timestamp()
426
+ ) OR (
427
+ U.state = 'deleting'
428
+ AND U.delete_claimed_at <= clock_timestamp() - (
429
+ greatest(p_claim_timeout_ms, 0)::double precision * interval '1 millisecond'
430
+ )
431
+ )
432
+ ORDER BY coalesce(U.delete_claimed_at, U.cleanup_after), U.id
433
+ LIMIT least(greatest(p_limit, 0), 1000)
434
+ ), locked_operations AS MATERIALIZED (
435
+ SELECT O.workspace_id, O.operation_id,
436
+ O.state AS operation_state, O.kind AS operation_kind
437
+ FROM %1$I.interaction_operations O
438
+ WHERE EXISTS (
439
+ SELECT 1 FROM due D
440
+ WHERE D.workspace_id = O.workspace_id
441
+ AND D.operation_id = O.operation_id
442
+ )
443
+ FOR UPDATE OF O SKIP LOCKED
444
+ ), candidates AS (
445
+ SELECT U.id, gen_random_uuid() AS claim_id, U.state AS upload_state,
446
+ O.operation_state, O.operation_kind
447
+ FROM due D
448
+ JOIN locked_operations O
449
+ ON O.workspace_id = D.workspace_id
450
+ AND O.operation_id = D.operation_id
451
+ JOIN %1$I.browser_state_uploads U ON U.id = D.id
452
+ WHERE (
453
+ U.state IN ('prepared', 'delete_pending')
454
+ AND U.cleanup_after <= clock_timestamp()
455
+ ) OR (
456
+ U.state = 'deleting'
457
+ AND U.delete_claimed_at <= clock_timestamp() - (
458
+ greatest(p_claim_timeout_ms, 0)::double precision * interval '1 millisecond'
459
+ )
460
+ )
461
+ ORDER BY coalesce(U.delete_claimed_at, U.cleanup_after), U.id
462
+ FOR UPDATE OF U SKIP LOCKED
463
+ ), claimed AS (
464
+ UPDATE %1$I.browser_state_uploads U
465
+ SET state = 'deleting', delete_claim_id = C.claim_id,
466
+ delete_claimed_at = clock_timestamp(), updated_at = clock_timestamp()
467
+ FROM candidates C
468
+ WHERE U.id = C.id
469
+ RETURNING U.*, C.claim_id, C.upload_state, C.operation_state, C.operation_kind
470
+ ), timed_out_operations AS (
471
+ UPDATE %1$I.interaction_operations O
472
+ SET state = CASE
473
+ WHEN C.operation_state = 'dispatched' THEN 'outcome_unknown'
474
+ ELSE 'failed'
475
+ END,
476
+ error_code = 'browser_state_upload_expired',
477
+ error_message = CASE
478
+ WHEN C.operation_state = 'dispatched'
479
+ THEN 'Browser state upload outcome was not published before its authority expired'
480
+ ELSE 'Browser state upload authority expired before dispatch'
481
+ END,
482
+ error_retryable = false, error_details = null,
483
+ settled_at = clock_timestamp(), updated_at = clock_timestamp()
484
+ FROM claimed C
485
+ WHERE C.upload_state = 'prepared'
486
+ AND O.workspace_id = C.workspace_id
487
+ AND O.operation_id = C.operation_id
488
+ AND O.state IN ('prepared', 'dispatched')
489
+ RETURNING O.account_id, O.workspace_id, O.resource_id, O.kind,
490
+ C.operation_state
491
+ ), timed_out_sessions AS (
492
+ UPDATE %1$I.browser_sessions S
493
+ SET lifecycle = CASE
494
+ WHEN T.operation_state = 'dispatched' THEN 'lost'
495
+ ELSE 'active'
496
+ END,
497
+ failure_code = CASE
498
+ WHEN T.operation_state = 'dispatched' THEN 'browser_state_upload_expired'
499
+ ELSE NULL
500
+ END,
501
+ updated_at = clock_timestamp()
502
+ FROM timed_out_operations T
503
+ WHERE T.kind = 'suspend'
504
+ AND S.workspace_id = T.workspace_id
505
+ AND S.id = T.resource_id
506
+ AND S.lifecycle = 'suspending'
507
+ RETURNING S.account_id, S.workspace_id
508
+ ), advanced_revisions AS (
509
+ UPDATE %1$I.workspace_interaction_revisions R
510
+ SET revision = R.revision + 1, updated_at = clock_timestamp()
511
+ FROM (
512
+ SELECT DISTINCT account_id, workspace_id FROM timed_out_sessions
513
+ ) S
514
+ WHERE R.account_id = S.account_id AND R.workspace_id = S.workspace_id
515
+ RETURNING R.workspace_id
516
+ )
517
+ SELECT C.claim_id, C.id, C.account_id, C.workspace_id, C.object_key
518
+ FROM claimed C;
519
+ $function$;
520
+ $create$, target_schema);
521
+ END $migration$;
522
+
523
+ REVOKE ALL ON FUNCTION opengeni_private.claim_browser_state_upload_cleanup(bigint, integer)
524
+ FROM PUBLIC;
525
+
526
+ DO $grants$
527
+ DECLARE target_schema text := current_schema();
528
+ BEGIN
529
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
530
+ GRANT EXECUTE ON FUNCTION opengeni_private.claim_browser_state_artifact_cleanup(bigint, integer)
531
+ TO opengeni_app;
532
+ GRANT EXECUTE ON FUNCTION opengeni_private.claim_browser_state_upload_cleanup(bigint, integer)
533
+ TO opengeni_app;
534
+ EXECUTE format(
535
+ 'REVOKE ALL ON TABLE %I.browser_identities, %I.browser_revisions, %I.browser_state_artifacts, %I.browser_state_uploads, %I.browser_revision_components FROM opengeni_app',
536
+ target_schema, target_schema, target_schema, target_schema, target_schema
537
+ );
538
+ EXECUTE format(
539
+ 'GRANT SELECT, INSERT, UPDATE ON TABLE %I.browser_identities TO opengeni_app',
540
+ target_schema
541
+ );
542
+ EXECUTE format(
543
+ 'GRANT SELECT, INSERT ON TABLE %I.browser_revisions TO opengeni_app',
544
+ target_schema
545
+ );
546
+ EXECUTE format(
547
+ 'GRANT SELECT, INSERT, UPDATE ON TABLE %I.browser_state_artifacts TO opengeni_app',
548
+ target_schema
549
+ );
550
+ EXECUTE format(
551
+ 'GRANT SELECT, INSERT, UPDATE ON TABLE %I.browser_state_uploads TO opengeni_app',
552
+ target_schema
553
+ );
554
+ EXECUTE format(
555
+ 'GRANT SELECT, INSERT ON TABLE %I.browser_revision_components TO opengeni_app',
556
+ target_schema
557
+ );
558
+ END IF;
559
+ END
560
+ $grants$;
@@ -0,0 +1,67 @@
1
+ -- deployment-mode: maintenance
2
+ -- Snapshot exact route authority into each BrowserSession and extend durable
3
+ -- interventions/operation receipts without rewriting migration 0210.
4
+ SET LOCAL lock_timeout = '5s';
5
+ SET LOCAL statement_timeout = '30s';
6
+
7
+ ALTER TABLE "browser_sessions"
8
+ ADD COLUMN "network_route_version" bigint,
9
+ ADD COLUMN "network_route_configuration" jsonb,
10
+ ADD COLUMN "network_route_consistency" jsonb,
11
+ ADD COLUMN "network_route_credential_version" bigint,
12
+ ADD COLUMN "network_route_authority_digest" text;
13
+
14
+ UPDATE "browser_sessions" S
15
+ SET "network_route_version" = R."version",
16
+ "network_route_configuration" = R."configuration",
17
+ "network_route_consistency" = R."consistency"
18
+ FROM "network_routes" R
19
+ WHERE S."workspace_id" = R."workspace_id"
20
+ AND S."network_route_id" = R."id";
21
+
22
+ ALTER TABLE "browser_sessions"
23
+ ADD CONSTRAINT "browser_sessions_network_route_authority_check" CHECK (
24
+ ("network_route_id" IS NULL
25
+ AND "network_route_version" IS NULL
26
+ AND "network_route_configuration" IS NULL
27
+ AND "network_route_consistency" IS NULL
28
+ AND "network_route_credential_version" IS NULL
29
+ AND "network_route_authority_digest" IS NULL)
30
+ OR
31
+ ("network_route_id" IS NOT NULL
32
+ AND "network_route_version" > 0
33
+ AND jsonb_typeof("network_route_configuration") = 'object'
34
+ AND octet_length("network_route_configuration"::text) BETWEEN 2 AND 65536
35
+ AND jsonb_typeof("network_route_consistency") = 'object'
36
+ AND octet_length("network_route_consistency"::text) BETWEEN 2 AND 65536
37
+ AND ("network_route_credential_version" IS NULL OR "network_route_credential_version" > 0)
38
+ AND ("network_route_authority_digest" IS NOT NULL OR "network_route_credential_version" IS NULL)
39
+ AND ("network_route_authority_digest" IS NULL OR (
40
+ octet_length("network_route_authority_digest") BETWEEN 16 AND 256
41
+ AND "network_route_authority_digest" ~ '^[A-Za-z0-9._~-]+$'
42
+ )))
43
+ );
44
+
45
+ CREATE INDEX "browser_sessions_workspace_network_route_idx"
46
+ ON "browser_sessions" ("workspace_id", "network_route_id", "lifecycle");
47
+
48
+ ALTER TABLE "interaction_interventions"
49
+ ADD COLUMN "originating_tool_call_id" text,
50
+ ADD CONSTRAINT "interaction_interventions_originating_tool_call_check" CHECK (
51
+ "originating_tool_call_id" IS NULL OR (
52
+ "originating_attempt_id" IS NOT NULL
53
+ AND octet_length("originating_tool_call_id") BETWEEN 1 AND 1024
54
+ )
55
+ );
56
+
57
+ CREATE UNIQUE INDEX "interaction_interventions_originating_tool_call_uq"
58
+ ON "interaction_interventions"
59
+ ("workspace_id", "originating_session_id", "originating_turn_id", "originating_tool_call_id")
60
+ WHERE "originating_tool_call_id" IS NOT NULL;
61
+
62
+ ALTER TABLE "interaction_resource_operations"
63
+ ADD COLUMN "metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
64
+ ADD CONSTRAINT "interaction_resource_operations_metadata_check" CHECK (
65
+ jsonb_typeof("metadata") = 'object'
66
+ AND octet_length("metadata"::text) BETWEEN 2 AND 65536
67
+ );
@@ -0,0 +1,34 @@
1
+ -- deployment-mode: maintenance
2
+ -- Clean-cut extension of the exact interaction-operation journal for browser
3
+ -- downloads. Replacing CHECK constraints requires a bounded maintenance window.
4
+ SET LOCAL lock_timeout = '5s';
5
+ SET LOCAL statement_timeout = '30s';
6
+
7
+ ALTER TABLE "interaction_resource_operations"
8
+ DROP CONSTRAINT "interaction_resource_operations_resource_kind_check";
9
+
10
+ ALTER TABLE "interaction_resource_operations"
11
+ ADD CONSTRAINT "interaction_resource_operations_resource_kind_check"
12
+ CHECK ("resource_kind" IN (
13
+ 'network_route',
14
+ 'site_auth_connection',
15
+ 'auth_run',
16
+ 'intervention',
17
+ 'browser_download'
18
+ ));
19
+
20
+ ALTER TABLE "interaction_resource_operations"
21
+ DROP CONSTRAINT "interaction_resource_operations_kind_check";
22
+
23
+ ALTER TABLE "interaction_resource_operations"
24
+ ADD CONSTRAINT "interaction_resource_operations_kind_check"
25
+ CHECK ("kind" IN (
26
+ 'create',
27
+ 'update',
28
+ 'start',
29
+ 'report',
30
+ 'protected_fill',
31
+ 'verify',
32
+ 'resolve',
33
+ 'save'
34
+ ));