@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,235 @@
1
+ -- deployment-mode: rolling
2
+ -- Additive Pack installation evidence and component ownership. Old API/worker
3
+ -- binaries continue to use the original columns; new binaries freeze the
4
+ -- reviewed manifest and populate the normalized component ledger.
5
+
6
+ ALTER TABLE "pack_installations"
7
+ ADD COLUMN "version" integer NOT NULL DEFAULT 1,
8
+ ADD COLUMN "manifest_snapshot" jsonb,
9
+ ADD COLUMN "manifest_digest" text,
10
+ ADD COLUMN "selected_rig_id" uuid REFERENCES "rigs"("id") ON DELETE SET NULL,
11
+ ADD COLUMN "installed_by_subject_id" text;
12
+
13
+ ALTER TABLE "pack_installations"
14
+ ADD CONSTRAINT "pack_installations_version_chk" CHECK ("version" > 0) NOT VALID,
15
+ ADD CONSTRAINT "pack_installations_manifest_chk" CHECK (
16
+ ("manifest_snapshot" IS NULL AND "manifest_digest" IS NULL)
17
+ OR (
18
+ jsonb_typeof("manifest_snapshot") = 'object'
19
+ AND octet_length(convert_to("manifest_snapshot"::text, 'UTF8')) <= 1048576
20
+ AND "manifest_digest" ~ '^[0-9a-f]{64}$'
21
+ )
22
+ ) NOT VALID,
23
+ ADD CONSTRAINT "pack_installations_actor_chk" CHECK (
24
+ "installed_by_subject_id" IS NULL
25
+ OR length(btrim("installed_by_subject_id")) BETWEEN 1 AND 1024
26
+ ) NOT VALID,
27
+ ADD CONSTRAINT "pack_installations_status_chk" CHECK (
28
+ "status" IN ('installing', 'active', 'needs_attention', 'disabled')
29
+ ) NOT VALID;
30
+
31
+ ALTER TABLE "pack_installations"
32
+ VALIDATE CONSTRAINT "pack_installations_version_chk";
33
+ ALTER TABLE "pack_installations"
34
+ VALIDATE CONSTRAINT "pack_installations_manifest_chk";
35
+ ALTER TABLE "pack_installations"
36
+ VALIDATE CONSTRAINT "pack_installations_actor_chk";
37
+ ALTER TABLE "pack_installations"
38
+ VALIDATE CONSTRAINT "pack_installations_status_chk";
39
+
40
+ CREATE INDEX "pack_installations_workspace_rig_idx"
41
+ ON "pack_installations" ("workspace_id", "selected_rig_id")
42
+ WHERE "selected_rig_id" IS NOT NULL;
43
+
44
+ CREATE TABLE "pack_installation_components" (
45
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
46
+ "account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
47
+ "workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
48
+ "pack_installation_id" uuid NOT NULL REFERENCES "pack_installations"("id") ON DELETE CASCADE,
49
+ "component_key" text NOT NULL,
50
+ "kind" text NOT NULL,
51
+ "capability_id" text NOT NULL,
52
+ "resolved_id" text NOT NULL,
53
+ "digest" text NOT NULL,
54
+ "metadata" jsonb NOT NULL DEFAULT '{}'::jsonb,
55
+ "created_at" timestamptz NOT NULL DEFAULT now(),
56
+ "updated_at" timestamptz NOT NULL DEFAULT now(),
57
+ CONSTRAINT "pack_installation_components_key_chk" CHECK (
58
+ length("component_key") BETWEEN 1 AND 128
59
+ AND "component_key" ~ '^[a-z0-9]([a-z0-9._/-]*[a-z0-9])?$'
60
+ ),
61
+ CONSTRAINT "pack_installation_components_kind_chk" CHECK (
62
+ "kind" IN ('plugin', 'skill', 'integration', 'feature', 'inline_skill')
63
+ ),
64
+ CONSTRAINT "pack_installation_components_identity_chk" CHECK (
65
+ length("capability_id") BETWEEN 1 AND 512
66
+ AND length("resolved_id") BETWEEN 1 AND 512
67
+ AND "capability_id" !~ '[[:cntrl:]]'
68
+ AND "resolved_id" !~ '[[:cntrl:]]'
69
+ AND "digest" ~ '^[0-9a-f]{64}$'
70
+ ),
71
+ CONSTRAINT "pack_installation_components_metadata_chk" CHECK (
72
+ jsonb_typeof("metadata") = 'object'
73
+ AND octet_length(convert_to("metadata"::text, 'UTF8')) <= 131072
74
+ ),
75
+ CONSTRAINT "pack_installation_components_pack_key_uq"
76
+ UNIQUE ("pack_installation_id", "component_key")
77
+ );
78
+
79
+ CREATE INDEX "pack_installation_components_workspace_capability_idx"
80
+ ON "pack_installation_components" ("workspace_id", "kind", "capability_id");
81
+
82
+ CREATE OR REPLACE FUNCTION pack_v2_validate_installation()
83
+ RETURNS trigger
84
+ LANGUAGE plpgsql
85
+ AS $$
86
+ BEGIN
87
+ IF NEW."manifest_snapshot" IS NOT NULL
88
+ AND NEW."manifest_snapshot" ->> 'id' IS DISTINCT FROM NEW."pack_id"
89
+ THEN
90
+ RAISE EXCEPTION 'pack installation snapshot id does not match pack_id'
91
+ USING ERRCODE = '23514';
92
+ END IF;
93
+ IF NEW."selected_rig_id" IS NOT NULL AND NOT EXISTS (
94
+ SELECT 1 FROM "rigs" r
95
+ WHERE r."id" = NEW."selected_rig_id"
96
+ AND r."account_id" = NEW."account_id"
97
+ AND r."workspace_id" = NEW."workspace_id"
98
+ ) THEN
99
+ RAISE EXCEPTION 'pack installation Rig belongs to another tenant'
100
+ USING ERRCODE = '23514';
101
+ END IF;
102
+ RETURN NEW;
103
+ END;
104
+ $$;
105
+
106
+ CREATE TRIGGER pack_installations_v2_validate
107
+ BEFORE INSERT OR UPDATE ON "pack_installations"
108
+ FOR EACH ROW EXECUTE FUNCTION pack_v2_validate_installation();
109
+
110
+ CREATE OR REPLACE FUNCTION pack_v2_validate_component()
111
+ RETURNS trigger
112
+ LANGUAGE plpgsql
113
+ AS $$
114
+ BEGIN
115
+ IF NOT EXISTS (
116
+ SELECT 1 FROM "pack_installations" i
117
+ WHERE i."id" = NEW."pack_installation_id"
118
+ AND i."account_id" = NEW."account_id"
119
+ AND i."workspace_id" = NEW."workspace_id"
120
+ ) THEN
121
+ RAISE EXCEPTION 'Pack component does not match its installation tenant'
122
+ USING ERRCODE = '23514';
123
+ END IF;
124
+ RETURN NEW;
125
+ END;
126
+ $$;
127
+
128
+ CREATE TRIGGER pack_installation_components_validate
129
+ BEFORE INSERT OR UPDATE ON "pack_installation_components"
130
+ FOR EACH ROW EXECUTE FUNCTION pack_v2_validate_component();
131
+
132
+ -- Harden the polymorphic owner identity added by 0202 now that first-class
133
+ -- Pack installation ids exist. Direct/migration owners remain textual, and
134
+ -- rolling legacy Pack owners remain valid when they are not UUID-shaped.
135
+ CREATE OR REPLACE FUNCTION capability_v2_validate_component_owner()
136
+ RETURNS trigger
137
+ LANGUAGE plpgsql
138
+ AS $$
139
+ BEGIN
140
+ IF NOT EXISTS (
141
+ SELECT 1 FROM "capability_facet_installations" i
142
+ WHERE i."id" = NEW."facet_installation_id"
143
+ AND i."account_id" = NEW."account_id"
144
+ AND i."workspace_id" = NEW."workspace_id"
145
+ ) THEN
146
+ RAISE EXCEPTION 'component owner does not match its facet installation tenant'
147
+ USING ERRCODE = '23514';
148
+ END IF;
149
+ IF NEW."owner_kind" = 'plugin' THEN
150
+ IF NEW."owner_id" !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$' THEN
151
+ RAISE EXCEPTION 'plugin component owner belongs to another tenant or does not exist'
152
+ USING ERRCODE = '23514';
153
+ ELSIF NOT EXISTS (
154
+ SELECT 1 FROM "capability_plugin_installations" p
155
+ WHERE p."id" = NEW."owner_id"::uuid
156
+ AND p."account_id" = NEW."account_id"
157
+ AND p."workspace_id" = NEW."workspace_id"
158
+ ) THEN
159
+ RAISE EXCEPTION 'plugin component owner belongs to another tenant or does not exist'
160
+ USING ERRCODE = '23514';
161
+ END IF;
162
+ ELSIF NEW."owner_kind" = 'pack'
163
+ AND NEW."owner_id" ~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
164
+ THEN
165
+ IF NOT EXISTS (
166
+ SELECT 1 FROM "pack_installations" p
167
+ WHERE p."id" = NEW."owner_id"::uuid
168
+ AND p."account_id" = NEW."account_id"
169
+ AND p."workspace_id" = NEW."workspace_id"
170
+ ) THEN
171
+ RAISE EXCEPTION 'Pack component owner belongs to another tenant or does not exist'
172
+ USING ERRCODE = '23514';
173
+ END IF;
174
+ END IF;
175
+ RETURN NEW;
176
+ END;
177
+ $$;
178
+
179
+ CREATE OR REPLACE FUNCTION capability_v2_validate_feature_binding_owner()
180
+ RETURNS trigger
181
+ LANGUAGE plpgsql
182
+ AS $$
183
+ BEGIN
184
+ IF NOT EXISTS (
185
+ SELECT 1 FROM "integration_feature_bindings" b
186
+ WHERE b."id" = NEW."binding_id"
187
+ AND b."account_id" = NEW."account_id"
188
+ AND b."workspace_id" = NEW."workspace_id"
189
+ ) THEN
190
+ RAISE EXCEPTION 'feature binding owner does not match its binding tenant'
191
+ USING ERRCODE = '23514';
192
+ END IF;
193
+ IF NEW."owner_kind" = 'plugin' THEN
194
+ IF NEW."owner_id" !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$' THEN
195
+ RAISE EXCEPTION 'plugin feature binding owner belongs to another tenant or does not exist'
196
+ USING ERRCODE = '23514';
197
+ ELSIF NOT EXISTS (
198
+ SELECT 1 FROM "capability_plugin_installations" p
199
+ WHERE p."id" = NEW."owner_id"::uuid
200
+ AND p."account_id" = NEW."account_id"
201
+ AND p."workspace_id" = NEW."workspace_id"
202
+ ) THEN
203
+ RAISE EXCEPTION 'plugin feature binding owner belongs to another tenant or does not exist'
204
+ USING ERRCODE = '23514';
205
+ END IF;
206
+ ELSIF NEW."owner_kind" = 'pack'
207
+ AND NEW."owner_id" ~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
208
+ THEN
209
+ IF NOT EXISTS (
210
+ SELECT 1 FROM "pack_installations" p
211
+ WHERE p."id" = NEW."owner_id"::uuid
212
+ AND p."account_id" = NEW."account_id"
213
+ AND p."workspace_id" = NEW."workspace_id"
214
+ ) THEN
215
+ RAISE EXCEPTION 'Pack feature binding owner belongs to another tenant or does not exist'
216
+ USING ERRCODE = '23514';
217
+ END IF;
218
+ END IF;
219
+ RETURN NEW;
220
+ END;
221
+ $$;
222
+
223
+ ALTER TABLE "pack_installation_components" ENABLE ROW LEVEL SECURITY;
224
+ ALTER TABLE "pack_installation_components" FORCE ROW LEVEL SECURITY;
225
+
226
+ CREATE POLICY workspace_isolation ON "pack_installation_components"
227
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
228
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
229
+
230
+ DO $$
231
+ BEGIN
232
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
233
+ GRANT SELECT, INSERT, UPDATE, DELETE ON "pack_installation_components" TO opengeni_app;
234
+ END IF;
235
+ END $$;
@@ -0,0 +1,32 @@
1
+ -- deployment-mode: rolling
2
+ -- Shared capability and Integration definitions are globally readable catalog
3
+ -- data, not tenant lifecycle rows. Revoke ordinary runtime deletion authority
4
+ -- without narrowing the installation, binding, owner, or operation lifecycle.
5
+
6
+ DO $$
7
+ BEGIN
8
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
9
+ REVOKE DELETE ON
10
+ "capability_plugins",
11
+ "capability_plugin_versions",
12
+ "capability_facets",
13
+ "capability_integration_facets",
14
+ "capability_mcp_facets",
15
+ "capability_api_facets",
16
+ "capability_skill_facets",
17
+ "capability_skill_files",
18
+ "integration_spec_revisions",
19
+ "integration_tools",
20
+ "integration_feature_facets"
21
+ FROM opengeni_app;
22
+
23
+ GRANT SELECT, INSERT, UPDATE, DELETE ON
24
+ "capability_plugin_installations",
25
+ "capability_facet_installations",
26
+ "capability_component_owners",
27
+ "integration_feature_bindings",
28
+ "integration_feature_binding_owners",
29
+ "capability_operations"
30
+ TO opengeni_app;
31
+ END IF;
32
+ END $$;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "0.28.1",
3
+ "version": "0.31.1",
4
4
  "description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -34,6 +34,18 @@
34
34
  "./provision-roles": {
35
35
  "types": "./dist/provision-roles.d.ts",
36
36
  "import": "./dist/provision-roles.js"
37
+ },
38
+ "./editable-artifacts": {
39
+ "types": "./dist/editable-artifacts.d.ts",
40
+ "import": "./dist/editable-artifacts.js"
41
+ },
42
+ "./editable-artifact-durable-export": {
43
+ "types": "./dist/editable-artifact-durable-export.d.ts",
44
+ "import": "./dist/editable-artifact-durable-export.js"
45
+ },
46
+ "./video-generation": {
47
+ "types": "./dist/video-generation.d.ts",
48
+ "import": "./dist/video-generation.js"
37
49
  }
38
50
  },
39
51
  "publishConfig": {
@@ -50,10 +62,11 @@
50
62
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
51
63
  },
52
64
  "dependencies": {
53
- "@opengeni/codex": "^0.2.11",
54
- "@opengeni/config": "^0.11.0",
55
- "@opengeni/contracts": "^0.39.0",
56
- "@opengeni/network": "^0.2.0",
65
+ "@opengeni/codemode": "^0.2.2",
66
+ "@opengeni/codex": "^0.2.15",
67
+ "@opengeni/config": "^0.13.2",
68
+ "@opengeni/contracts": "^0.44.1",
69
+ "@opengeni/network": "^0.2.2",
57
70
  "drizzle-orm": "^0.45.2",
58
71
  "postgres": "^3.4.7"
59
72
  },
@@ -0,0 +1,371 @@
1
+ import {
2
+ AttachedBrowserDevice,
3
+ AttachedBrowserDeviceCapabilities,
4
+ AttachedBrowserDeviceListResponse,
5
+ AttachedBrowserInventorySnapshot,
6
+ type AttachedBrowserDevice as AttachedBrowserDeviceValue,
7
+ type AttachedBrowserDeviceAnnouncement,
8
+ type AttachedBrowserDeviceListResponse as AttachedBrowserDeviceListResponseValue,
9
+ type AttachedBrowserInventorySnapshot as AttachedBrowserInventorySnapshotValue,
10
+ } from "@opengeni/contracts";
11
+ import { and, asc, desc, eq, inArray, isNull } from "drizzle-orm";
12
+ import { type Database, withRlsContext } from "./database";
13
+ import {
14
+ advanceWorkspaceInteractionRevision,
15
+ readWorkspaceInteractionRevision,
16
+ } from "./interaction-revisions";
17
+ import * as schema from "./schema";
18
+
19
+ type DeviceRow = typeof schema.attachedBrowserDevices.$inferSelect;
20
+
21
+ const CONSISTENT_READ = {
22
+ isolationLevel: "repeatable read",
23
+ accessMode: "read only",
24
+ } as const;
25
+
26
+ export class AttachedBrowserDeviceNotFoundError extends Error {
27
+ readonly name = "AttachedBrowserDeviceNotFoundError";
28
+ }
29
+
30
+ export class AttachedBrowserInventoryConflictError extends Error {
31
+ readonly name = "AttachedBrowserInventoryConflictError";
32
+ }
33
+
34
+ export type ReconcileAttachedBrowserInventoryResult = {
35
+ accepted: boolean;
36
+ changed: boolean;
37
+ revision: number;
38
+ };
39
+
40
+ function safeInteger(value: number, label: string): number {
41
+ if (!Number.isSafeInteger(value) || value < 0) throw new Error(`${label} is outside safe range`);
42
+ return value;
43
+ }
44
+
45
+ function deviceFromRow(row: DeviceRow): AttachedBrowserDeviceValue {
46
+ return AttachedBrowserDevice.parse({
47
+ id: row.id,
48
+ accountId: row.accountId,
49
+ workspaceId: row.workspaceId,
50
+ enrollmentId: row.enrollmentId,
51
+ name: row.name,
52
+ profileLabel: row.profileLabel,
53
+ browserName: row.browserName,
54
+ browserVersion: row.browserVersion,
55
+ extensionVersion: row.extensionVersion,
56
+ platform: row.platform,
57
+ architecture: row.architecture,
58
+ state: row.disconnectedAt ? "disconnected" : "connected",
59
+ connectionGeneration: row.connectionGeneration,
60
+ inventoryRevision: safeInteger(row.inventoryRevision, "attached browser inventory revision"),
61
+ tabCount: safeInteger(row.tabCount, "attached browser tab count"),
62
+ capabilities: row.capabilities,
63
+ lastSeenAt: row.lastSeenAt.toISOString(),
64
+ disconnectedAt: row.disconnectedAt?.toISOString() ?? null,
65
+ createdAt: row.createdAt.toISOString(),
66
+ updatedAt: row.updatedAt.toISOString(),
67
+ });
68
+ }
69
+
70
+ function sameAnnouncement(row: DeviceRow, value: AttachedBrowserDeviceAnnouncement): boolean {
71
+ return (
72
+ row.name === value.name &&
73
+ row.profileLabel === value.profileLabel &&
74
+ row.browserName === value.browserName &&
75
+ row.browserVersion === value.browserVersion &&
76
+ row.extensionVersion === value.extensionVersion &&
77
+ row.platform === value.platform &&
78
+ row.architecture === value.architecture &&
79
+ row.connectionGeneration === value.connectionGeneration &&
80
+ row.inventoryRevision === value.inventoryRevision &&
81
+ row.tabCount === value.tabCount &&
82
+ JSON.stringify(AttachedBrowserDeviceCapabilities.parse(row.capabilities)) ===
83
+ JSON.stringify(value.capabilities) &&
84
+ row.disconnectedAt === null
85
+ );
86
+ }
87
+
88
+ /** Reconcile one full browser-bridge snapshot. The enrollment row serializes
89
+ * concurrent heartbeats; a lower revision from the same bridge generation is
90
+ * ignored, and a replay only refreshes liveness. */
91
+ export async function reconcileAttachedBrowserInventory(
92
+ db: Database,
93
+ input: {
94
+ accountId: string;
95
+ workspaceId: string;
96
+ enrollmentId: string;
97
+ snapshot: AttachedBrowserInventorySnapshotValue;
98
+ },
99
+ ): Promise<ReconcileAttachedBrowserInventoryResult> {
100
+ const snapshot = AttachedBrowserInventorySnapshot.parse(input.snapshot);
101
+ return await withRlsContext(
102
+ db,
103
+ input,
104
+ async (scopedDb) =>
105
+ await scopedDb.transaction(async (rawTx) => {
106
+ const tx = rawTx as unknown as Database;
107
+ const [enrollment] = await tx
108
+ .select({
109
+ id: schema.enrollments.id,
110
+ accountId: schema.enrollments.accountId,
111
+ workspaceId: schema.enrollments.workspaceId,
112
+ status: schema.enrollments.status,
113
+ })
114
+ .from(schema.enrollments)
115
+ .where(
116
+ and(
117
+ eq(schema.enrollments.id, input.enrollmentId),
118
+ eq(schema.enrollments.accountId, input.accountId),
119
+ eq(schema.enrollments.workspaceId, input.workspaceId),
120
+ ),
121
+ )
122
+ .for("update")
123
+ .limit(1);
124
+ if (!enrollment || enrollment.status !== "active") {
125
+ throw new AttachedBrowserInventoryConflictError(
126
+ "attached browser inventory belongs to an unavailable enrollment",
127
+ );
128
+ }
129
+
130
+ const [cursor] = await tx
131
+ .select()
132
+ .from(schema.attachedBrowserInventories)
133
+ .where(
134
+ and(
135
+ eq(schema.attachedBrowserInventories.workspaceId, input.workspaceId),
136
+ eq(schema.attachedBrowserInventories.enrollmentId, input.enrollmentId),
137
+ ),
138
+ )
139
+ .for("update")
140
+ .limit(1);
141
+ if (
142
+ cursor?.bridgeGeneration === snapshot.bridgeGeneration &&
143
+ snapshot.revision < cursor.revision
144
+ ) {
145
+ return {
146
+ accepted: false,
147
+ changed: false,
148
+ revision: await readWorkspaceInteractionRevision(tx, input.workspaceId),
149
+ };
150
+ }
151
+
152
+ const now = new Date();
153
+ const rows = await tx
154
+ .select()
155
+ .from(schema.attachedBrowserDevices)
156
+ .where(eq(schema.attachedBrowserDevices.workspaceId, input.workspaceId))
157
+ .for("update");
158
+ const byId = new Map(rows.map((row) => [row.id, row]));
159
+ let changed = false;
160
+ for (const device of snapshot.devices) {
161
+ const existing = byId.get(device.id);
162
+ if (existing && existing.enrollmentId !== input.enrollmentId) {
163
+ throw new AttachedBrowserInventoryConflictError(
164
+ "attached browser device id is already owned by another enrollment",
165
+ );
166
+ }
167
+ if (!existing) {
168
+ await tx.insert(schema.attachedBrowserDevices).values({
169
+ id: device.id,
170
+ accountId: input.accountId,
171
+ workspaceId: input.workspaceId,
172
+ enrollmentId: input.enrollmentId,
173
+ name: device.name,
174
+ profileLabel: device.profileLabel,
175
+ browserName: device.browserName,
176
+ browserVersion: device.browserVersion,
177
+ extensionVersion: device.extensionVersion,
178
+ platform: device.platform,
179
+ architecture: device.architecture,
180
+ connectionGeneration: device.connectionGeneration,
181
+ inventoryRevision: device.inventoryRevision,
182
+ tabCount: device.tabCount,
183
+ capabilities: device.capabilities,
184
+ lastSeenAt: now,
185
+ disconnectedAt: null,
186
+ createdAt: now,
187
+ updatedAt: now,
188
+ });
189
+ changed = true;
190
+ continue;
191
+ }
192
+ const materialChanged = !sameAnnouncement(existing, device);
193
+ await tx
194
+ .update(schema.attachedBrowserDevices)
195
+ .set({
196
+ name: device.name,
197
+ profileLabel: device.profileLabel,
198
+ browserName: device.browserName,
199
+ browserVersion: device.browserVersion,
200
+ extensionVersion: device.extensionVersion,
201
+ platform: device.platform,
202
+ architecture: device.architecture,
203
+ connectionGeneration: device.connectionGeneration,
204
+ inventoryRevision: device.inventoryRevision,
205
+ tabCount: device.tabCount,
206
+ capabilities: device.capabilities,
207
+ lastSeenAt: now,
208
+ disconnectedAt: null,
209
+ updatedAt: materialChanged ? now : existing.updatedAt,
210
+ })
211
+ .where(
212
+ and(
213
+ eq(schema.attachedBrowserDevices.workspaceId, input.workspaceId),
214
+ eq(schema.attachedBrowserDevices.id, device.id),
215
+ ),
216
+ );
217
+ changed ||= materialChanged;
218
+ }
219
+
220
+ const announced = new Set(snapshot.devices.map((device) => device.id));
221
+ const disconnectedIds = rows
222
+ .filter(
223
+ (row) =>
224
+ row.enrollmentId === input.enrollmentId &&
225
+ row.disconnectedAt === null &&
226
+ !announced.has(row.id),
227
+ )
228
+ .map((row) => row.id);
229
+ if (disconnectedIds.length > 0) {
230
+ await tx
231
+ .update(schema.attachedBrowserDevices)
232
+ .set({ disconnectedAt: now, updatedAt: now })
233
+ .where(
234
+ and(
235
+ eq(schema.attachedBrowserDevices.workspaceId, input.workspaceId),
236
+ eq(schema.attachedBrowserDevices.enrollmentId, input.enrollmentId),
237
+ inArray(schema.attachedBrowserDevices.id, disconnectedIds),
238
+ isNull(schema.attachedBrowserDevices.disconnectedAt),
239
+ ),
240
+ );
241
+ changed = true;
242
+ }
243
+
244
+ if (cursor) {
245
+ await tx
246
+ .update(schema.attachedBrowserInventories)
247
+ .set({
248
+ bridgeGeneration: snapshot.bridgeGeneration,
249
+ revision: snapshot.revision,
250
+ observedAt: now,
251
+ updatedAt: now,
252
+ })
253
+ .where(
254
+ and(
255
+ eq(schema.attachedBrowserInventories.workspaceId, input.workspaceId),
256
+ eq(schema.attachedBrowserInventories.enrollmentId, input.enrollmentId),
257
+ ),
258
+ );
259
+ } else {
260
+ await tx.insert(schema.attachedBrowserInventories).values({
261
+ accountId: input.accountId,
262
+ workspaceId: input.workspaceId,
263
+ enrollmentId: input.enrollmentId,
264
+ bridgeGeneration: snapshot.bridgeGeneration,
265
+ revision: snapshot.revision,
266
+ observedAt: now,
267
+ updatedAt: now,
268
+ });
269
+ }
270
+ const revision = changed
271
+ ? await advanceWorkspaceInteractionRevision(tx, input.accountId, input.workspaceId)
272
+ : await readWorkspaceInteractionRevision(tx, input.workspaceId);
273
+ return { accepted: true, changed, revision };
274
+ }),
275
+ );
276
+ }
277
+
278
+ /** Immediately mark every live Chrome profile on a cleanly disconnected agent
279
+ * offline. Historical endpoint rows remain discoverable and reconnect in place. */
280
+ export async function disconnectAttachedBrowserDevices(
281
+ db: Database,
282
+ input: { accountId: string; workspaceId: string; enrollmentId: string },
283
+ ): Promise<{ changed: boolean; revision: number }> {
284
+ return await withRlsContext(
285
+ db,
286
+ input,
287
+ async (scopedDb) =>
288
+ await scopedDb.transaction(async (rawTx) => {
289
+ const tx = rawTx as unknown as Database;
290
+ const now = new Date();
291
+ const rows = await tx
292
+ .update(schema.attachedBrowserDevices)
293
+ .set({ disconnectedAt: now, updatedAt: now })
294
+ .where(
295
+ and(
296
+ eq(schema.attachedBrowserDevices.workspaceId, input.workspaceId),
297
+ eq(schema.attachedBrowserDevices.enrollmentId, input.enrollmentId),
298
+ isNull(schema.attachedBrowserDevices.disconnectedAt),
299
+ ),
300
+ )
301
+ .returning({ id: schema.attachedBrowserDevices.id });
302
+ const revision = rows.length
303
+ ? await advanceWorkspaceInteractionRevision(tx, input.accountId, input.workspaceId)
304
+ : await readWorkspaceInteractionRevision(tx, input.workspaceId);
305
+ return { changed: rows.length > 0, revision };
306
+ }),
307
+ );
308
+ }
309
+
310
+ export async function listAttachedBrowserDevices(
311
+ db: Database,
312
+ input: {
313
+ accountId: string;
314
+ workspaceId: string;
315
+ includeDisconnected?: boolean;
316
+ },
317
+ ): Promise<AttachedBrowserDeviceListResponseValue> {
318
+ return await withRlsContext(
319
+ db,
320
+ input,
321
+ async (scopedDb) => {
322
+ const rows = await scopedDb
323
+ .select()
324
+ .from(schema.attachedBrowserDevices)
325
+ .where(
326
+ input.includeDisconnected
327
+ ? eq(schema.attachedBrowserDevices.workspaceId, input.workspaceId)
328
+ : and(
329
+ eq(schema.attachedBrowserDevices.workspaceId, input.workspaceId),
330
+ isNull(schema.attachedBrowserDevices.disconnectedAt),
331
+ ),
332
+ )
333
+ .orderBy(
334
+ desc(schema.attachedBrowserDevices.disconnectedAt),
335
+ desc(schema.attachedBrowserDevices.updatedAt),
336
+ asc(schema.attachedBrowserDevices.name),
337
+ asc(schema.attachedBrowserDevices.id),
338
+ );
339
+ return AttachedBrowserDeviceListResponse.parse({
340
+ revision: await readWorkspaceInteractionRevision(scopedDb, input.workspaceId),
341
+ devices: rows.map(deviceFromRow),
342
+ });
343
+ },
344
+ CONSISTENT_READ,
345
+ );
346
+ }
347
+
348
+ export async function getAttachedBrowserDevice(
349
+ db: Database,
350
+ input: { accountId: string; workspaceId: string; deviceId: string },
351
+ ): Promise<AttachedBrowserDeviceValue> {
352
+ return await withRlsContext(
353
+ db,
354
+ input,
355
+ async (scopedDb) => {
356
+ const [row] = await scopedDb
357
+ .select()
358
+ .from(schema.attachedBrowserDevices)
359
+ .where(
360
+ and(
361
+ eq(schema.attachedBrowserDevices.workspaceId, input.workspaceId),
362
+ eq(schema.attachedBrowserDevices.id, input.deviceId),
363
+ ),
364
+ )
365
+ .limit(1);
366
+ if (!row) throw new AttachedBrowserDeviceNotFoundError("Attached browser not found");
367
+ return deviceFromRow(row);
368
+ },
369
+ CONSISTENT_READ,
370
+ );
371
+ }