@opengeni/db 0.28.1 → 0.31.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 (187) hide show
  1. package/dist/attached-browser-devices.d.ts +42 -0
  2. package/dist/attempt-tool-catalogs.d.ts +16 -0
  3. package/dist/browser-auth.d.ts +91 -0
  4. package/dist/browser-identities.d.ts +128 -0
  5. package/dist/browser-sessions.d.ts +272 -0
  6. package/dist/browser-state-artifacts.d.ts +17 -0
  7. package/dist/capability-components.d.ts +37 -0
  8. package/dist/capability-integrations.d.ts +192 -0
  9. package/dist/chunk-F5FT7BEZ.js +3535 -0
  10. package/dist/chunk-F5FT7BEZ.js.map +1 -0
  11. package/dist/chunk-ISI2TIUG.js +639 -0
  12. package/dist/chunk-ISI2TIUG.js.map +1 -0
  13. package/dist/chunk-MWDVXQOL.js +11639 -0
  14. package/dist/chunk-MWDVXQOL.js.map +1 -0
  15. package/dist/chunk-NBE6CHRI.js +955 -0
  16. package/dist/chunk-NBE6CHRI.js.map +1 -0
  17. package/dist/{chunk-DBS5HPEW.js → chunk-YHZXQ7HP.js} +760 -11
  18. package/dist/chunk-YHZXQ7HP.js.map +1 -0
  19. package/dist/codemode-operations.d.ts +117 -0
  20. package/dist/codex-token-resolver.d.ts +2 -2
  21. package/dist/company-profile-schema.d.ts +798 -0
  22. package/dist/company-profile.d.ts +268 -0
  23. package/dist/computer-sessions.d.ts +109 -0
  24. package/dist/connection-token-resolver.d.ts +9 -4
  25. package/dist/database.d.ts +52 -3
  26. package/dist/editable-artifact-durable-export.d.ts +153 -0
  27. package/dist/editable-artifact-durable-export.js +788 -0
  28. package/dist/editable-artifact-durable-export.js.map +1 -0
  29. package/dist/editable-artifact-materialization.d.ts +123 -0
  30. package/dist/editable-artifacts-schema.d.ts +4152 -0
  31. package/dist/editable-artifacts.d.ts +610 -0
  32. package/dist/editable-artifacts.js +26 -0
  33. package/dist/editable-artifacts.js.map +1 -0
  34. package/dist/environment-crypto.d.ts +8 -4
  35. package/dist/generated-images.d.ts +139 -0
  36. package/dist/index.d.ts +958 -126
  37. package/dist/index.js +31204 -12172
  38. package/dist/index.js.map +1 -1
  39. package/dist/insights.d.ts +51 -4
  40. package/dist/integration-bindings.d.ts +70 -0
  41. package/dist/integration-features.d.ts +99 -0
  42. package/dist/interaction-revisions.d.ts +3 -0
  43. package/dist/interaction-schema.d.ts +5372 -0
  44. package/dist/knowledge-source-sync-schema.d.ts +1679 -0
  45. package/dist/knowledge-source-sync.d.ts +274 -0
  46. package/dist/lossless-columns.d.ts +58 -0
  47. package/dist/lossless-json.d.ts +39 -0
  48. package/dist/memory-domain.d.ts +3 -6
  49. package/dist/memory-governance.d.ts +2 -2
  50. package/dist/memory-slack-delivery.d.ts +101 -0
  51. package/dist/new-session-drafts.d.ts +2 -2
  52. package/dist/pack-components.d.ts +76 -0
  53. package/dist/pack-installations.d.ts +86 -0
  54. package/dist/persistence-errors.d.ts +7 -14
  55. package/dist/plugin-packages.d.ts +126 -0
  56. package/dist/preference-registry.d.ts +1 -1
  57. package/dist/provision-roles.d.ts +9 -1
  58. package/dist/provision-roles.js +1 -1
  59. package/dist/runtime-posture.d.ts +18 -6
  60. package/dist/schema.d.ts +19122 -8731
  61. package/dist/schema.js +173 -1
  62. package/dist/scoped-knowledge.d.ts +97 -1
  63. package/dist/session-control.d.ts +4 -4
  64. package/dist/session-queue-commands.d.ts +19 -11
  65. package/dist/session-realtime-context.d.ts +2 -2
  66. package/dist/session-realtime-ledger.d.ts +4 -4
  67. package/dist/session-realtime-mirror.d.ts +2 -2
  68. package/dist/session-realtime-state.d.ts +1 -1
  69. package/dist/session-realtime-terminal.d.ts +8 -1
  70. package/dist/session-realtime.d.ts +7 -7
  71. package/dist/session-tool-call-settlement.d.ts +1 -1
  72. package/dist/slack-user-link-access.d.ts +54 -0
  73. package/dist/transcription-recordings-schema.d.ts +44 -6
  74. package/dist/transcription-recordings.d.ts +3 -3
  75. package/dist/turn-initiator.d.ts +2 -2
  76. package/dist/video-generation.d.ts +216 -0
  77. package/dist/video-generation.js +66 -0
  78. package/dist/video-generation.js.map +1 -0
  79. package/dist/workspace-artifacts.d.ts +1 -1
  80. package/dist/workspace-instruction-policies.d.ts +1 -1
  81. package/dist/workspace-learning-policy-schema.d.ts +932 -0
  82. package/dist/workspace-learning-policy.d.ts +161 -0
  83. package/drizzle/0065_enrollment_credential_generation.sql +10 -0
  84. package/drizzle/0140_retained_screenshot_artifacts.sql +192 -0
  85. package/drizzle/0176_lossless_canonical_json.sql +975 -0
  86. package/drizzle/0177_session_events_workspace_turn_type_index.sql +5 -0
  87. package/drizzle/0178_permissioned_secret_reads.sql +14 -0
  88. package/drizzle/0179_slack_private_shortcut_delivery_gate.sql +85 -0
  89. package/drizzle/0180_retained_screenshot_lifecycle_fences.sql +273 -0
  90. package/drizzle/0181_connected_machine_removal.sql +52 -0
  91. package/drizzle/0182_connected_machine_remove_session_default.sql +77 -0
  92. package/drizzle/0183_model_call_provider_cost_estimates.sql +39 -0
  93. package/drizzle/0184_sandbox_drain_teardown_fence.sql +682 -0
  94. package/drizzle/0185_temporal_schedule_cleanup_outbox.sql +168 -0
  95. package/drizzle/0186_sandbox_capture_provider_contract.sql +400 -0
  96. package/drizzle/0187_generated_image_artifacts.sql +173 -0
  97. package/drizzle/0188_image_generation_retention_failure.sql +25 -0
  98. package/drizzle/0189_retained_session_image_formats.sql +13 -0
  99. package/drizzle/0190_timeline_annotations.sql +31 -0
  100. package/drizzle/0191_editable_artifact_engine.sql +3853 -0
  101. package/drizzle/0192_editable_artifact_live_tickets.sql +338 -0
  102. package/drizzle/0193_editable_artifact_authorization.sql +190 -0
  103. package/drizzle/0194_editable_artifact_durable_exports.sql +160 -0
  104. package/drizzle/0195_editable_artifact_import_authorization.sql +203 -0
  105. package/drizzle/0196_rig_provider_images.sql +152 -0
  106. package/drizzle/0197_knowledge_source_sync_schedules.sql +523 -0
  107. package/drizzle/0198_memory_slack_publication_delivery.sql +393 -0
  108. package/drizzle/0199_workspace_learning_policy.sql +980 -0
  109. package/drizzle/0201_company_profile_authority.sql +849 -0
  110. package/drizzle/0202_document_index_checkpoints.sql +85 -0
  111. package/drizzle/0203_durable_video_generation.sql +353 -0
  112. package/drizzle/0204_video_generation_funding.sql +44 -0
  113. package/drizzle/0205_attempt_tool_catalogs.sql +230 -0
  114. package/drizzle/0206_browser_sessions.sql +646 -0
  115. package/drizzle/0207_browser_identities.sql +379 -0
  116. package/drizzle/0208_attached_browser_devices.sql +165 -0
  117. package/drizzle/0209_computer_sessions.sql +575 -0
  118. package/drizzle/0210_browser_auth_network_interventions.sql +441 -0
  119. package/drizzle/0211_editable_artifact_session_links.sql +58 -0
  120. package/drizzle/0212_slack_installation_bindings.sql +348 -0
  121. package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
  122. package/drizzle/0214_session_activity_commit_gate.sql +262 -0
  123. package/drizzle/0215_capabilities_platform.sql +980 -0
  124. package/drizzle/0216_pack_component_ownership.sql +235 -0
  125. package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
  126. package/package.json +18 -5
  127. package/src/attached-browser-devices.ts +371 -0
  128. package/src/attempt-tool-catalogs.ts +131 -0
  129. package/src/browser-auth.ts +1896 -0
  130. package/src/browser-identities.ts +1168 -0
  131. package/src/browser-sessions.ts +2005 -0
  132. package/src/browser-state-artifacts.ts +59 -0
  133. package/src/capability-components.ts +166 -0
  134. package/src/capability-integrations.ts +1663 -0
  135. package/src/codemode-operations.ts +674 -0
  136. package/src/company-profile-schema.ts +143 -0
  137. package/src/company-profile.ts +968 -0
  138. package/src/computer-sessions.ts +1130 -0
  139. package/src/connection-token-resolver.ts +270 -25
  140. package/src/database.ts +434 -24
  141. package/src/editable-artifact-durable-export.ts +1201 -0
  142. package/src/editable-artifact-materialization.ts +893 -0
  143. package/src/editable-artifacts-schema.ts +879 -0
  144. package/src/editable-artifacts.ts +5404 -0
  145. package/src/environment-crypto.ts +22 -9
  146. package/src/generated-images.ts +740 -0
  147. package/src/index.ts +10117 -2730
  148. package/src/insights.ts +160 -13
  149. package/src/integration-bindings.ts +455 -0
  150. package/src/integration-features.ts +859 -0
  151. package/src/interaction-revisions.ts +40 -0
  152. package/src/interaction-schema.ts +1303 -0
  153. package/src/knowledge-source-sync-schema.ts +192 -0
  154. package/src/knowledge-source-sync.ts +1922 -0
  155. package/src/lossless-columns.ts +35 -0
  156. package/src/lossless-json.ts +322 -0
  157. package/src/memory-domain.ts +5 -44
  158. package/src/memory-slack-delivery.ts +801 -0
  159. package/src/pack-components.ts +954 -0
  160. package/src/pack-installations.ts +929 -0
  161. package/src/persistence-errors.ts +26 -34
  162. package/src/plugin-packages.ts +1186 -0
  163. package/src/provision-roles.ts +302 -1
  164. package/src/runtime-posture-cli.ts +9 -0
  165. package/src/runtime-posture.ts +514 -5
  166. package/src/schema.ts +5177 -2957
  167. package/src/scoped-knowledge-schema.ts +1 -1
  168. package/src/scoped-knowledge.ts +655 -3
  169. package/src/session-control.ts +133 -81
  170. package/src/session-queue-commands.ts +511 -247
  171. package/src/session-realtime-context.ts +20 -7
  172. package/src/session-realtime-ledger.ts +32 -10
  173. package/src/session-realtime-mirror.ts +4 -2
  174. package/src/session-realtime-terminal.ts +89 -21
  175. package/src/session-realtime.ts +10 -9
  176. package/src/session-tool-call-settlement.ts +38 -15
  177. package/src/slack-user-link-access.ts +702 -0
  178. package/src/transcription-recordings-schema.ts +5 -2
  179. package/src/transcription-recordings.ts +70 -40
  180. package/src/video-generation.ts +1452 -0
  181. package/src/workspace-learning-policy-schema.ts +139 -0
  182. package/src/workspace-learning-policy.ts +595 -0
  183. package/dist/chunk-DBS5HPEW.js.map +0 -1
  184. package/dist/chunk-HT5T62CC.js +0 -6803
  185. package/dist/chunk-HT5T62CC.js.map +0 -1
  186. package/dist/event-payload-sanitizer.d.ts +0 -32
  187. package/src/event-payload-sanitizer.ts +0 -377
@@ -0,0 +1,646 @@
1
+ -- deployment-mode: maintenance
2
+ -- Canonical BrowserSession resource and interaction-holder authority. Every
3
+ -- application/worker writer is upgraded together; no legacy holder protocol remains.
4
+ SET LOCAL lock_timeout = '5s';
5
+ SET LOCAL statement_timeout = '30s';
6
+
7
+ CREATE TABLE "workspace_interaction_revisions" (
8
+ "workspace_id" uuid PRIMARY KEY,
9
+ "account_id" uuid NOT NULL,
10
+ "revision" bigint NOT NULL DEFAULT 0,
11
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
12
+ CONSTRAINT "workspace_interaction_revisions_workspace_account_fk"
13
+ FOREIGN KEY ("workspace_id", "account_id")
14
+ REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE,
15
+ CONSTRAINT "workspace_interaction_revisions_revision_check"
16
+ CHECK ("revision" >= 0)
17
+ );
18
+
19
+ CREATE TABLE "browser_sessions" (
20
+ "id" uuid PRIMARY KEY,
21
+ "account_id" uuid NOT NULL,
22
+ "workspace_id" uuid NOT NULL,
23
+ "name" text NOT NULL,
24
+ "lifecycle" text NOT NULL DEFAULT 'starting',
25
+ "placement_kind" text NOT NULL,
26
+ "sandbox_group_id" uuid,
27
+ "connected_sandbox_id" uuid,
28
+ "device_id" uuid,
29
+ "external_provider_id" text,
30
+ "external_placement_id" text,
31
+ "controller_id" text,
32
+ "controller_generation" text,
33
+ "placement_instance_id" text,
34
+ "token_generation" integer NOT NULL DEFAULT 1,
35
+ "driver_id" text NOT NULL,
36
+ "engine" text NOT NULL,
37
+ "engine_version" text,
38
+ "headless" boolean NOT NULL,
39
+ "identity_id" uuid,
40
+ "base_revision_id" uuid,
41
+ "linked_computer_session_id" uuid,
42
+ "network_route_id" uuid,
43
+ "private_checkpoint_artifact_id" uuid,
44
+ "capabilities" jsonb NOT NULL,
45
+ "create_operation_id" uuid NOT NULL,
46
+ "created_by_subject_id" text NOT NULL,
47
+ "failure_code" text,
48
+ "controller_heartbeat_at" timestamptz,
49
+ "created_at" timestamptz NOT NULL DEFAULT now(),
50
+ "last_used_at" timestamptz NOT NULL DEFAULT now(),
51
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
52
+ CONSTRAINT "browser_sessions_workspace_account_fk"
53
+ FOREIGN KEY ("workspace_id", "account_id")
54
+ REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE,
55
+ CONSTRAINT "browser_sessions_lifecycle_check" CHECK (
56
+ "lifecycle" IN (
57
+ 'starting', 'active', 'suspending', 'suspended', 'restoring',
58
+ 'repair_required', 'lost', 'ending', 'ended', 'failed'
59
+ )
60
+ ),
61
+ CONSTRAINT "browser_sessions_engine_check" CHECK (
62
+ "engine" IN ('chromium', 'chrome', 'firefox', 'webkit', 'lightpanda', 'external')
63
+ ),
64
+ CONSTRAINT "browser_sessions_values_check" CHECK (
65
+ octet_length("name") BETWEEN 1 AND 200
66
+ AND octet_length("driver_id") BETWEEN 1 AND 512
67
+ AND octet_length("created_by_subject_id") BETWEEN 1 AND 1024
68
+ AND "token_generation" > 0
69
+ AND jsonb_typeof("capabilities") = 'object'
70
+ AND octet_length("capabilities"::text) BETWEEN 2 AND 65536
71
+ AND ("engine_version" IS NULL OR octet_length("engine_version") BETWEEN 1 AND 256)
72
+ AND ("failure_code" IS NULL OR octet_length("failure_code") BETWEEN 1 AND 512)
73
+ ),
74
+ CONSTRAINT "browser_sessions_placement_check" CHECK (
75
+ (
76
+ "placement_kind" = 'sandbox_group'
77
+ AND "sandbox_group_id" IS NOT NULL
78
+ AND "connected_sandbox_id" IS NULL
79
+ AND "device_id" IS NULL
80
+ AND "external_provider_id" IS NULL
81
+ AND "external_placement_id" IS NULL
82
+ ) OR (
83
+ "placement_kind" = 'connected_machine'
84
+ AND "sandbox_group_id" IS NULL
85
+ AND "connected_sandbox_id" IS NOT NULL
86
+ AND "device_id" IS NULL
87
+ AND "external_provider_id" IS NULL
88
+ AND "external_placement_id" IS NULL
89
+ ) OR (
90
+ "placement_kind" = 'attached_device'
91
+ AND "sandbox_group_id" IS NULL
92
+ AND "connected_sandbox_id" IS NULL
93
+ AND "device_id" IS NOT NULL
94
+ AND "external_provider_id" IS NULL
95
+ AND "external_placement_id" IS NULL
96
+ ) OR (
97
+ "placement_kind" = 'external_provider'
98
+ AND "sandbox_group_id" IS NULL
99
+ AND "connected_sandbox_id" IS NULL
100
+ AND "device_id" IS NULL
101
+ AND "external_provider_id" IS NOT NULL
102
+ AND "external_placement_id" IS NOT NULL
103
+ AND octet_length("external_provider_id") BETWEEN 1 AND 512
104
+ AND octet_length("external_placement_id") BETWEEN 1 AND 512
105
+ )
106
+ ),
107
+ CONSTRAINT "browser_sessions_controller_check" CHECK (
108
+ (
109
+ "controller_id" IS NULL
110
+ AND "controller_generation" IS NULL
111
+ AND "placement_instance_id" IS NULL
112
+ AND "controller_heartbeat_at" IS NULL
113
+ ) OR (
114
+ "controller_id" IS NOT NULL
115
+ AND octet_length("controller_id") BETWEEN 1 AND 512
116
+ AND "controller_generation" IS NOT NULL
117
+ AND octet_length("controller_generation") BETWEEN 1 AND 256
118
+ AND "placement_instance_id" IS NOT NULL
119
+ AND octet_length("placement_instance_id") BETWEEN 1 AND 512
120
+ AND "controller_heartbeat_at" IS NOT NULL
121
+ )
122
+ ),
123
+ CONSTRAINT "browser_sessions_identity_revision_check"
124
+ CHECK ("base_revision_id" IS NULL OR "identity_id" IS NOT NULL),
125
+ CONSTRAINT "browser_sessions_failure_check" CHECK (
126
+ (
127
+ "lifecycle" IN ('repair_required', 'lost', 'failed')
128
+ AND "failure_code" IS NOT NULL
129
+ ) OR (
130
+ "lifecycle" NOT IN ('repair_required', 'lost', 'failed')
131
+ AND "failure_code" IS NULL
132
+ )
133
+ ),
134
+ CONSTRAINT "browser_sessions_workspace_id_uq" UNIQUE ("workspace_id", "id"),
135
+ CONSTRAINT "browser_sessions_workspace_create_operation_uq"
136
+ UNIQUE ("workspace_id", "create_operation_id")
137
+ );
138
+
139
+ CREATE INDEX "browser_sessions_workspace_lifecycle_idx"
140
+ ON "browser_sessions" ("workspace_id", "lifecycle", "last_used_at", "id");
141
+ CREATE INDEX "browser_sessions_sandbox_group_idx"
142
+ ON "browser_sessions" ("workspace_id", "sandbox_group_id", "lifecycle");
143
+
144
+ CREATE TABLE "browser_session_associations" (
145
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
146
+ "account_id" uuid NOT NULL,
147
+ "workspace_id" uuid NOT NULL,
148
+ "browser_session_id" uuid NOT NULL,
149
+ "session_id" uuid NOT NULL,
150
+ "turn_id" uuid,
151
+ "attempt_id" uuid,
152
+ "relationship" text NOT NULL,
153
+ "actor_subject_id" text NOT NULL,
154
+ "last_used_at" timestamptz NOT NULL DEFAULT now(),
155
+ "created_at" timestamptz NOT NULL DEFAULT now(),
156
+ CONSTRAINT "browser_session_associations_workspace_account_fk"
157
+ FOREIGN KEY ("workspace_id", "account_id")
158
+ REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE,
159
+ CONSTRAINT "browser_session_associations_resource_fk"
160
+ FOREIGN KEY ("workspace_id", "browser_session_id")
161
+ REFERENCES "browser_sessions"("workspace_id", "id") ON DELETE CASCADE,
162
+ CONSTRAINT "browser_session_associations_session_fk"
163
+ FOREIGN KEY ("workspace_id", "session_id")
164
+ REFERENCES "sessions"("workspace_id", "id") ON DELETE CASCADE,
165
+ CONSTRAINT "browser_session_associations_turn_fk"
166
+ FOREIGN KEY ("workspace_id", "turn_id")
167
+ REFERENCES "session_turns"("workspace_id", "id") ON DELETE SET NULL ("turn_id"),
168
+ CONSTRAINT "browser_session_associations_attempt_fk"
169
+ FOREIGN KEY ("workspace_id", "attempt_id")
170
+ REFERENCES "session_turn_attempts"("workspace_id", "id") ON DELETE SET NULL ("attempt_id"),
171
+ CONSTRAINT "browser_session_associations_relationship_check"
172
+ CHECK ("relationship" IN ('created', 'using', 'observing', 'related')),
173
+ CONSTRAINT "browser_session_associations_values_check" CHECK (
174
+ octet_length("actor_subject_id") BETWEEN 1 AND 1024
175
+ AND ("attempt_id" IS NULL OR "turn_id" IS NOT NULL)
176
+ ),
177
+ CONSTRAINT "browser_session_associations_resource_session_relationship_uq"
178
+ UNIQUE ("browser_session_id", "session_id", "relationship")
179
+ );
180
+
181
+ CREATE INDEX "browser_session_associations_workspace_session_idx"
182
+ ON "browser_session_associations" ("workspace_id", "session_id", "last_used_at");
183
+ CREATE INDEX "browser_session_associations_resource_idx"
184
+ ON "browser_session_associations" ("workspace_id", "browser_session_id", "last_used_at");
185
+
186
+ CREATE TABLE "interaction_operations" (
187
+ "operation_id" uuid PRIMARY KEY,
188
+ "account_id" uuid NOT NULL,
189
+ "workspace_id" uuid NOT NULL,
190
+ "resource_kind" text NOT NULL,
191
+ "resource_id" uuid NOT NULL,
192
+ "kind" text NOT NULL,
193
+ "request_digest" text NOT NULL,
194
+ "state" text NOT NULL DEFAULT 'prepared',
195
+ "controller_generation" text,
196
+ "error_code" text,
197
+ "error_message" text,
198
+ "error_retryable" boolean,
199
+ "error_details" jsonb,
200
+ "actor_subject_id" text NOT NULL,
201
+ "created_at" timestamptz NOT NULL DEFAULT now(),
202
+ "dispatched_at" timestamptz,
203
+ "settled_at" timestamptz,
204
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
205
+ CONSTRAINT "interaction_operations_workspace_account_fk"
206
+ FOREIGN KEY ("workspace_id", "account_id")
207
+ REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE,
208
+ CONSTRAINT "interaction_operations_resource_kind_check"
209
+ CHECK ("resource_kind" IN ('browser_session', 'computer_session')),
210
+ CONSTRAINT "interaction_operations_kind_check"
211
+ CHECK ("kind" IN ('create', 'resume', 'suspend', 'end')),
212
+ CONSTRAINT "interaction_operations_state_check"
213
+ CHECK ("state" IN ('prepared', 'dispatched', 'completed', 'failed', 'outcome_unknown')),
214
+ CONSTRAINT "interaction_operations_values_check" CHECK (
215
+ "request_digest" ~ '^[0-9a-f]{64}$'
216
+ AND octet_length("actor_subject_id") BETWEEN 1 AND 1024
217
+ AND ("controller_generation" IS NULL OR octet_length("controller_generation") BETWEEN 1 AND 256)
218
+ AND ("error_code" IS NULL OR octet_length("error_code") BETWEEN 1 AND 512)
219
+ AND ("error_message" IS NULL OR octet_length("error_message") BETWEEN 1 AND 8192)
220
+ AND ("error_details" IS NULL OR (
221
+ jsonb_typeof("error_details") = 'object'
222
+ AND octet_length("error_details"::text) BETWEEN 2 AND 65536
223
+ ))
224
+ ),
225
+ CONSTRAINT "interaction_operations_lifecycle_check" CHECK (
226
+ (
227
+ "state" = 'prepared'
228
+ AND "dispatched_at" IS NULL
229
+ AND "settled_at" IS NULL
230
+ AND "error_code" IS NULL
231
+ AND "error_message" IS NULL
232
+ AND "error_retryable" IS NULL
233
+ AND "error_details" IS NULL
234
+ ) OR (
235
+ "state" = 'dispatched'
236
+ AND "dispatched_at" IS NOT NULL
237
+ AND "settled_at" IS NULL
238
+ AND "error_code" IS NULL
239
+ AND "error_message" IS NULL
240
+ AND "error_retryable" IS NULL
241
+ AND "error_details" IS NULL
242
+ ) OR (
243
+ "state" = 'completed'
244
+ AND "dispatched_at" IS NOT NULL
245
+ AND "settled_at" IS NOT NULL
246
+ AND "error_code" IS NULL
247
+ AND "error_message" IS NULL
248
+ AND "error_retryable" IS NULL
249
+ AND "error_details" IS NULL
250
+ ) OR (
251
+ "state" IN ('failed', 'outcome_unknown')
252
+ AND "settled_at" IS NOT NULL
253
+ AND "error_code" IS NOT NULL
254
+ AND "error_message" IS NOT NULL
255
+ AND "error_retryable" IS NOT NULL
256
+ )
257
+ ),
258
+ CONSTRAINT "interaction_operations_workspace_operation_uq"
259
+ UNIQUE ("workspace_id", "operation_id")
260
+ );
261
+
262
+ CREATE INDEX "interaction_operations_resource_history_idx"
263
+ ON "interaction_operations" (
264
+ "workspace_id", "resource_kind", "resource_id", "created_at"
265
+ );
266
+ CREATE UNIQUE INDEX "interaction_operations_active_resource_uq"
267
+ ON "interaction_operations" ("workspace_id", "resource_kind", "resource_id")
268
+ WHERE "state" IN ('prepared', 'dispatched');
269
+
270
+ ALTER TABLE "browser_sessions"
271
+ ADD CONSTRAINT "browser_sessions_create_operation_fk"
272
+ FOREIGN KEY ("workspace_id", "create_operation_id")
273
+ REFERENCES "interaction_operations"("workspace_id", "operation_id") ON DELETE RESTRICT;
274
+
275
+ ALTER TABLE "sandbox_lease_holders"
276
+ DROP CONSTRAINT "sandbox_lease_holders_kind_check";
277
+ ALTER TABLE "sandbox_lease_holders"
278
+ ADD CONSTRAINT "sandbox_lease_holders_kind_check"
279
+ CHECK ("kind" IN ('turn', 'viewer', 'direct', 'process', 'interaction'));
280
+
281
+ ALTER TABLE "workspace_interaction_revisions" ENABLE ROW LEVEL SECURITY;
282
+ ALTER TABLE "workspace_interaction_revisions" FORCE ROW LEVEL SECURITY;
283
+ CREATE POLICY workspace_isolation ON "workspace_interaction_revisions"
284
+ USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
285
+ WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
286
+
287
+ ALTER TABLE "browser_sessions" ENABLE ROW LEVEL SECURITY;
288
+ ALTER TABLE "browser_sessions" FORCE ROW LEVEL SECURITY;
289
+ CREATE POLICY workspace_isolation ON "browser_sessions"
290
+ USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
291
+ WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
292
+
293
+ ALTER TABLE "browser_session_associations" ENABLE ROW LEVEL SECURITY;
294
+ ALTER TABLE "browser_session_associations" FORCE ROW LEVEL SECURITY;
295
+ CREATE POLICY workspace_isolation ON "browser_session_associations"
296
+ USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
297
+ WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
298
+
299
+ ALTER TABLE "interaction_operations" ENABLE ROW LEVEL SECURITY;
300
+ ALTER TABLE "interaction_operations" FORCE ROW LEVEL SECURITY;
301
+ CREATE POLICY workspace_isolation ON "interaction_operations"
302
+ USING (opengeni_private.workspace_rls_visible("account_id", "workspace_id"))
303
+ WITH CHECK (opengeni_private.workspace_rls_visible("account_id", "workspace_id"));
304
+
305
+ DO $grants$
306
+ DECLARE target_schema text := current_schema();
307
+ BEGIN
308
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
309
+ EXECUTE format(
310
+ 'REVOKE ALL ON TABLE %I.workspace_interaction_revisions, %I.browser_sessions, %I.browser_session_associations, %I.interaction_operations FROM opengeni_app',
311
+ target_schema,
312
+ target_schema,
313
+ target_schema,
314
+ target_schema
315
+ );
316
+ EXECUTE format(
317
+ 'GRANT SELECT, INSERT, UPDATE ON TABLE %I.workspace_interaction_revisions TO opengeni_app',
318
+ target_schema
319
+ );
320
+ EXECUTE format(
321
+ 'GRANT SELECT, INSERT, UPDATE ON TABLE %I.browser_sessions TO opengeni_app',
322
+ target_schema
323
+ );
324
+ EXECUTE format(
325
+ 'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.browser_session_associations TO opengeni_app',
326
+ target_schema
327
+ );
328
+ EXECUTE format(
329
+ 'GRANT SELECT, INSERT, UPDATE ON TABLE %I.interaction_operations TO opengeni_app',
330
+ target_schema
331
+ );
332
+ END IF;
333
+ END
334
+ $grants$;
335
+
336
+ -- The mega-PR is a maintenance cutover, so the global reaper has one exact
337
+ -- four-argument contract. Interaction holders are controller-heartbeat-bound;
338
+ -- a dead controller makes its BrowserSession visibly lost before the box drains.
339
+ DROP FUNCTION opengeni_private.reap_sandbox_leases(bigint, bigint, bigint);
340
+
341
+ DO $interaction_aware_reaper$
342
+ DECLARE data_schema text := current_schema();
343
+ BEGIN
344
+ EXECUTE format($create$
345
+ CREATE FUNCTION opengeni_private.reap_sandbox_leases(
346
+ p_viewer_holder_ttl_ms bigint,
347
+ p_turn_holder_ttl_ms bigint,
348
+ p_interaction_holder_ttl_ms bigint,
349
+ p_idle_grace_ms bigint
350
+ )
351
+ RETURNS TABLE (
352
+ workspace_id uuid,
353
+ sandbox_group_id uuid,
354
+ instance_id text,
355
+ lease_epoch integer
356
+ )
357
+ LANGUAGE plpgsql
358
+ SECURITY DEFINER
359
+ SET search_path = pg_catalog
360
+ AS $function$
361
+ DECLARE
362
+ locked_ids uuid[];
363
+ stale_interaction_ids uuid[];
364
+ changed_interaction_workspaces uuid[];
365
+ BEGIN
366
+ PERFORM pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
367
+
368
+ SELECT coalesce(pg_catalog.array_agg(candidate.id), ARRAY[]::uuid[])
369
+ INTO locked_ids
370
+ FROM (
371
+ SELECT lease.id
372
+ FROM %1$I.sandbox_leases lease
373
+ WHERE lease.liveness <> 'cold'
374
+ OR EXISTS (
375
+ SELECT 1
376
+ FROM %1$I.sandbox_lease_holders holder
377
+ WHERE holder.lease_id = lease.id
378
+ )
379
+ ORDER BY lease.updated_at, lease.id
380
+ LIMIT 500
381
+ FOR UPDATE OF lease SKIP LOCKED
382
+ ) candidate;
383
+
384
+ IF pg_catalog.cardinality(locked_ids) = 0 THEN
385
+ RETURN;
386
+ END IF;
387
+
388
+ DELETE FROM %1$I.sandbox_lease_holders holder
389
+ USING (
390
+ SELECT stale.id
391
+ FROM %1$I.sandbox_lease_holders stale
392
+ WHERE stale.lease_id = ANY(locked_ids)
393
+ AND stale.kind IN ('viewer', 'direct')
394
+ AND stale.last_heartbeat_at
395
+ < pg_catalog.now()
396
+ - pg_catalog.make_interval(secs => p_viewer_holder_ttl_ms / 1000.0)
397
+ FOR UPDATE OF stale SKIP LOCKED
398
+ ) victim
399
+ WHERE holder.id = victim.id;
400
+
401
+ IF p_turn_holder_ttl_ms > 0 THEN
402
+ DELETE FROM %1$I.sandbox_lease_holders holder
403
+ USING (
404
+ SELECT stale.id
405
+ FROM %1$I.sandbox_lease_holders stale
406
+ WHERE stale.lease_id = ANY(locked_ids)
407
+ AND stale.kind = 'turn'
408
+ AND (
409
+ stale.last_heartbeat_at
410
+ < pg_catalog.now()
411
+ - pg_catalog.make_interval(secs => p_turn_holder_ttl_ms / 1000.0)
412
+ OR (
413
+ stale.holder_id ~* '^turn-attempt:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
414
+ AND NOT EXISTS (
415
+ SELECT 1
416
+ FROM %1$I.session_turn_attempts attempt
417
+ JOIN %1$I.session_turns turn
418
+ ON turn.account_id = attempt.account_id
419
+ AND turn.workspace_id = attempt.workspace_id
420
+ AND turn.session_id = attempt.session_id
421
+ AND turn.id = attempt.turn_id
422
+ AND turn.execution_generation = attempt.execution_generation
423
+ AND turn.active_attempt_id = attempt.id
424
+ WHERE stale.holder_id = ('turn-attempt:' || attempt.id::text)
425
+ AND attempt.account_id = stale.account_id
426
+ AND attempt.workspace_id = stale.workspace_id
427
+ AND attempt.session_id = stale.subject_id
428
+ AND attempt.state IN ('claimed', 'running')
429
+ )
430
+ )
431
+ )
432
+ FOR UPDATE OF stale SKIP LOCKED
433
+ ) victim
434
+ WHERE holder.id = victim.id;
435
+ END IF;
436
+
437
+ IF p_interaction_holder_ttl_ms > 0 THEN
438
+ SELECT coalesce(pg_catalog.array_agg(candidate.id), ARRAY[]::uuid[])
439
+ INTO stale_interaction_ids
440
+ FROM (
441
+ SELECT stale.id
442
+ FROM %1$I.sandbox_lease_holders stale
443
+ WHERE stale.lease_id = ANY(locked_ids)
444
+ AND stale.kind = 'interaction'
445
+ AND (
446
+ stale.last_heartbeat_at
447
+ < pg_catalog.now()
448
+ - pg_catalog.make_interval(
449
+ secs => p_interaction_holder_ttl_ms / 1000.0
450
+ )
451
+ OR NOT EXISTS (
452
+ SELECT 1
453
+ FROM %1$I.browser_sessions browser
454
+ WHERE stale.holder_id = ('browser-session:' || browser.id::text)
455
+ AND browser.account_id = stale.account_id
456
+ AND browser.workspace_id = stale.workspace_id
457
+ AND browser.sandbox_group_id = (
458
+ SELECT lease.sandbox_group_id
459
+ FROM %1$I.sandbox_leases lease
460
+ WHERE lease.id = stale.lease_id
461
+ )
462
+ AND browser.lifecycle IN (
463
+ 'starting', 'active', 'suspending', 'restoring', 'ending'
464
+ )
465
+ )
466
+ )
467
+ FOR UPDATE OF stale SKIP LOCKED
468
+ ) candidate;
469
+
470
+ IF pg_catalog.cardinality(stale_interaction_ids) > 0 THEN
471
+ WITH updated AS (
472
+ UPDATE %1$I.browser_sessions browser
473
+ SET lifecycle = 'lost',
474
+ controller_id = null,
475
+ controller_generation = null,
476
+ placement_instance_id = null,
477
+ controller_heartbeat_at = null,
478
+ failure_code = 'controller_heartbeat_expired',
479
+ updated_at = pg_catalog.now()
480
+ FROM %1$I.sandbox_lease_holders holder
481
+ WHERE holder.id = ANY(stale_interaction_ids)
482
+ AND holder.holder_id = ('browser-session:' || browser.id::text)
483
+ AND holder.account_id = browser.account_id
484
+ AND holder.workspace_id = browser.workspace_id
485
+ AND browser.lifecycle IN (
486
+ 'starting', 'active', 'suspending', 'restoring', 'ending'
487
+ )
488
+ RETURNING browser.workspace_id
489
+ )
490
+ SELECT coalesce(
491
+ pg_catalog.array_agg(DISTINCT updated.workspace_id),
492
+ ARRAY[]::uuid[]
493
+ )
494
+ INTO changed_interaction_workspaces
495
+ FROM updated;
496
+
497
+ DELETE FROM %1$I.sandbox_lease_holders holder
498
+ WHERE holder.id = ANY(stale_interaction_ids);
499
+
500
+ UPDATE %1$I.workspace_interaction_revisions revision
501
+ SET revision = revision.revision + 1,
502
+ updated_at = pg_catalog.now()
503
+ WHERE revision.workspace_id = ANY(changed_interaction_workspaces);
504
+ END IF;
505
+ END IF;
506
+
507
+ UPDATE %1$I.sandbox_leases lease SET
508
+ refcount = counts.total,
509
+ turn_holders = counts.turns,
510
+ viewer_holders = counts.viewers,
511
+ liveness = CASE
512
+ WHEN lease.liveness = 'warm'
513
+ AND counts.total = 0
514
+ AND counts.turns = 0
515
+ AND (
516
+ lease.reaper_hold_id IS NULL
517
+ OR lease.reaper_hold_until <= pg_catalog.now()
518
+ )
519
+ THEN 'draining' ELSE lease.liveness END,
520
+ expires_at = CASE
521
+ WHEN lease.liveness = 'warm'
522
+ AND counts.total = 0
523
+ AND counts.turns = 0
524
+ AND (
525
+ lease.reaper_hold_id IS NULL
526
+ OR lease.reaper_hold_until <= pg_catalog.now()
527
+ )
528
+ THEN CASE
529
+ WHEN lease.rotation_requested_at IS NOT NULL
530
+ OR lease.archive_capture_id IS NOT NULL
531
+ THEN pg_catalog.now() - interval '1 millisecond'
532
+ ELSE pg_catalog.now()
533
+ + pg_catalog.make_interval(secs => p_idle_grace_ms / 1000.0)
534
+ END
535
+ ELSE lease.expires_at END,
536
+ updated_at = pg_catalog.now()
537
+ FROM (
538
+ SELECT candidate.id,
539
+ pg_catalog.count(holder.id)::int AS total,
540
+ pg_catalog.count(holder.id)
541
+ FILTER (WHERE holder.kind = 'turn')::int AS turns,
542
+ pg_catalog.count(holder.id)
543
+ FILTER (WHERE holder.kind = 'viewer')::int AS viewers
544
+ FROM pg_catalog.unnest(locked_ids) candidate(id)
545
+ LEFT JOIN %1$I.sandbox_lease_holders holder
546
+ ON holder.lease_id = candidate.id
547
+ GROUP BY candidate.id
548
+ ) counts
549
+ WHERE lease.id = counts.id;
550
+
551
+ UPDATE %1$I.sandbox_leases lease SET
552
+ liveness = 'cold',
553
+ instance_id = null,
554
+ lease_epoch = lease.lease_epoch + 1,
555
+ reaper_hold_id = null,
556
+ reaper_hold_until = null,
557
+ reaper_hold_reason = null,
558
+ resume_state = opengeni_private.warming_reset_resume_state_v2(
559
+ lease.backend,
560
+ lease.resume_backend_id,
561
+ lease.resume_state,
562
+ lease.workspace_generation,
563
+ lease.archive_generation,
564
+ pg_catalog.clock_timestamp()
565
+ ),
566
+ resume_backend_id = CASE
567
+ WHEN coalesce(
568
+ lease.resume_state #>> '{sessionState,workspaceArchive}', ''
569
+ ) <> ''
570
+ OR coalesce(
571
+ lease.resume_state #>> '{sessionState,workspaceArchivePrev}', ''
572
+ ) <> ''
573
+ THEN coalesce(lease.resume_backend_id, lease.backend)
574
+ ELSE null END,
575
+ data_plane_url = null,
576
+ terminal_data_plane_url = null,
577
+ provider_created_at = null,
578
+ provider_deadline_at = null,
579
+ rotation_requested_at = null,
580
+ rotation_reason = null,
581
+ updated_at = pg_catalog.now()
582
+ WHERE lease.id = ANY(locked_ids)
583
+ AND lease.liveness = 'warming'
584
+ AND lease.expires_at < pg_catalog.now()
585
+ AND lease.instance_id IS NULL
586
+ AND (
587
+ lease.reaper_hold_id IS NULL
588
+ OR lease.reaper_hold_until <= pg_catalog.now()
589
+ );
590
+
591
+ UPDATE %1$I.sandbox_leases lease SET
592
+ liveness = 'draining',
593
+ refcount = 0,
594
+ turn_holders = 0,
595
+ viewer_holders = 0,
596
+ data_plane_url = null,
597
+ terminal_data_plane_url = null,
598
+ lease_epoch = lease.lease_epoch + 1,
599
+ reaper_hold_id = null,
600
+ reaper_hold_until = null,
601
+ reaper_hold_reason = null,
602
+ expires_at = pg_catalog.now() - interval '1 millisecond',
603
+ updated_at = pg_catalog.now()
604
+ WHERE lease.id = ANY(locked_ids)
605
+ AND lease.liveness = 'warming'
606
+ AND lease.expires_at < pg_catalog.now()
607
+ AND lease.instance_id IS NOT NULL
608
+ AND (
609
+ lease.reaper_hold_id IS NULL
610
+ OR lease.reaper_hold_until <= pg_catalog.now()
611
+ );
612
+
613
+ RETURN QUERY
614
+ SELECT lease.workspace_id, lease.sandbox_group_id,
615
+ lease.instance_id, lease.lease_epoch
616
+ FROM %1$I.sandbox_leases lease
617
+ WHERE lease.id = ANY(locked_ids)
618
+ AND lease.liveness = 'draining'
619
+ AND lease.expires_at < pg_catalog.now()
620
+ AND lease.refcount = 0
621
+ AND (
622
+ lease.reaper_hold_id IS NULL
623
+ OR lease.reaper_hold_until <= pg_catalog.now()
624
+ );
625
+ END;
626
+ $function$;
627
+ $create$, data_schema);
628
+ END
629
+ $interaction_aware_reaper$;
630
+
631
+ REVOKE ALL ON FUNCTION opengeni_private.reap_sandbox_leases(
632
+ bigint, bigint, bigint, bigint
633
+ ) FROM PUBLIC;
634
+
635
+ DO $reaper_grant$
636
+ BEGIN
637
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
638
+ GRANT EXECUTE ON FUNCTION opengeni_private.reap_sandbox_leases(
639
+ bigint, bigint, bigint, bigint
640
+ ) TO opengeni_app;
641
+ END IF;
642
+ END
643
+ $reaper_grant$;
644
+
645
+ RESET statement_timeout;
646
+ RESET lock_timeout;