@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,400 @@
1
+ -- deployment-mode: rolling
2
+ -- Add an immutable, workspace-owned authority for shared Slack conversation
3
+ -- participation and result publication. No existing non-shared Slack path is
4
+ -- changed by this migration.
5
+
6
+ SET LOCAL lock_timeout = '5s';
7
+ SET LOCAL statement_timeout = '10min';
8
+
9
+ CREATE SEQUENCE slack_task_policy_revision_seq AS bigint;
10
+
11
+ CREATE OR REPLACE FUNCTION slack_task_policy_valid(value jsonb)
12
+ RETURNS boolean LANGUAGE plpgsql IMMUTABLE AS $$
13
+ DECLARE
14
+ key text;
15
+ ids jsonb;
16
+ BEGIN
17
+ IF jsonb_typeof(value) IS DISTINCT FROM 'object'
18
+ OR (SELECT count(*) FROM jsonb_object_keys(value)) <> 7
19
+ OR NOT (
20
+ value ? 'allowedTeamIds'
21
+ AND value ? 'allowedConversationIds'
22
+ AND value ? 'allowGuestInitiators'
23
+ AND value ? 'allowExternalInitiators'
24
+ AND value ? 'allowMpim'
25
+ AND value ? 'sharedConversationMode'
26
+ AND value ? 'resultPublicationMode'
27
+ )
28
+ OR jsonb_typeof(value->'allowedTeamIds') IS DISTINCT FROM 'array'
29
+ OR jsonb_typeof(value->'allowedConversationIds') IS DISTINCT FROM 'array'
30
+ OR jsonb_array_length(value->'allowedTeamIds') > 256
31
+ OR jsonb_array_length(value->'allowedConversationIds') > 256
32
+ OR jsonb_typeof(value->'allowGuestInitiators') IS DISTINCT FROM 'boolean'
33
+ OR jsonb_typeof(value->'allowExternalInitiators') IS DISTINCT FROM 'boolean'
34
+ OR jsonb_typeof(value->'allowMpim') IS DISTINCT FROM 'boolean'
35
+ OR value->>'sharedConversationMode' NOT IN ('deny', 'private_handoff')
36
+ OR value->>'resultPublicationMode' NOT IN ('never', 'approval_required', 'allow')
37
+ OR octet_length(convert_to(value::text, 'UTF8')) > 65536
38
+ THEN
39
+ RETURN false;
40
+ END IF;
41
+
42
+ FOREACH key IN ARRAY ARRAY['allowedTeamIds', 'allowedConversationIds'] LOOP
43
+ ids := value->key;
44
+ IF EXISTS (
45
+ SELECT 1 FROM jsonb_array_elements(ids) item
46
+ WHERE jsonb_typeof(item) IS DISTINCT FROM 'string'
47
+ OR length(btrim(item #>> '{}')) NOT BETWEEN 1 AND 128
48
+ OR item #>> '{}' IS DISTINCT FROM btrim(item #>> '{}')
49
+ ) OR (
50
+ SELECT count(DISTINCT item #>> '{}') FROM jsonb_array_elements(ids) item
51
+ ) <> jsonb_array_length(ids) THEN
52
+ RETURN false;
53
+ END IF;
54
+ END LOOP;
55
+ RETURN true;
56
+ END;
57
+ $$;
58
+
59
+ CREATE OR REPLACE FUNCTION slack_task_policy_hash(policy jsonb)
60
+ RETURNS text LANGUAGE sql IMMUTABLE AS $$
61
+ SELECT encode(sha256(convert_to(jsonb_build_array('slack_task_policy', 1, policy)::text, 'UTF8')), 'hex');
62
+ $$;
63
+
64
+ REVOKE ALL ON FUNCTION slack_task_policy_valid(jsonb) FROM PUBLIC;
65
+ REVOKE ALL ON FUNCTION slack_task_policy_hash(jsonb) FROM PUBLIC;
66
+
67
+ CREATE TABLE slack_task_policy_revisions (
68
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
69
+ operation_id uuid NOT NULL,
70
+ request_fingerprint text NOT NULL,
71
+ account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
72
+ workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
73
+ revision bigint NOT NULL DEFAULT nextval('slack_task_policy_revision_seq'),
74
+ policy jsonb NOT NULL,
75
+ policy_hash text NOT NULL,
76
+ supersedes_revision_id uuid,
77
+ created_by_subject_id text NOT NULL,
78
+ created_at timestamptz NOT NULL DEFAULT now(),
79
+ CONSTRAINT slack_task_policy_revisions_revision_chk CHECK (revision > 0),
80
+ CONSTRAINT slack_task_policy_revisions_policy_chk CHECK (
81
+ slack_task_policy_valid(policy) AND policy_hash = slack_task_policy_hash(policy)
82
+ ),
83
+ CONSTRAINT slack_task_policy_revisions_receipt_chk CHECK (request_fingerprint ~ '^[0-9a-f]{64}$'),
84
+ CONSTRAINT slack_task_policy_revisions_actor_chk CHECK (
85
+ length(btrim(created_by_subject_id)) BETWEEN 1 AND 1024
86
+ ),
87
+ CONSTRAINT slack_task_policy_revisions_workspace_operation_uq UNIQUE (workspace_id, operation_id),
88
+ CONSTRAINT slack_task_policy_revisions_workspace_revision_uq UNIQUE (workspace_id, revision),
89
+ CONSTRAINT slack_task_policy_revisions_workspace_identity_uq UNIQUE (account_id, workspace_id, id),
90
+ CONSTRAINT slack_task_policy_revisions_supersedes_fk
91
+ FOREIGN KEY (account_id, workspace_id, supersedes_revision_id)
92
+ REFERENCES slack_task_policy_revisions(account_id, workspace_id, id) ON DELETE RESTRICT
93
+ );
94
+
95
+ CREATE INDEX slack_task_policy_revisions_workspace_history_idx
96
+ ON slack_task_policy_revisions(workspace_id, revision DESC);
97
+
98
+ CREATE TABLE slack_task_policy_heads (
99
+ account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
100
+ workspace_id uuid PRIMARY KEY REFERENCES workspaces(id) ON DELETE CASCADE,
101
+ revision_id uuid NOT NULL,
102
+ revision bigint NOT NULL,
103
+ policy_hash text NOT NULL,
104
+ activation_version bigint NOT NULL,
105
+ activated_at timestamptz NOT NULL DEFAULT now(),
106
+ CONSTRAINT slack_task_policy_heads_revision_fk
107
+ FOREIGN KEY (account_id, workspace_id, revision_id)
108
+ REFERENCES slack_task_policy_revisions(account_id, workspace_id, id) ON DELETE RESTRICT,
109
+ CONSTRAINT slack_task_policy_heads_version_chk CHECK (
110
+ revision > 0 AND activation_version > 0 AND policy_hash ~ '^[0-9a-f]{64}$'
111
+ )
112
+ );
113
+
114
+ CREATE INDEX slack_task_policy_heads_account_idx ON slack_task_policy_heads(account_id);
115
+
116
+ CREATE TABLE slack_task_policy_activation_events (
117
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
118
+ operation_id uuid NOT NULL,
119
+ request_fingerprint text NOT NULL,
120
+ account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
121
+ workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
122
+ activation_version bigint NOT NULL,
123
+ old_revision_id uuid,
124
+ old_revision bigint,
125
+ old_policy_hash text,
126
+ new_revision_id uuid NOT NULL,
127
+ new_revision bigint NOT NULL,
128
+ new_policy_hash text NOT NULL,
129
+ actor_subject_id text NOT NULL,
130
+ reason text NOT NULL,
131
+ created_at timestamptz NOT NULL DEFAULT now(),
132
+ CONSTRAINT slack_task_policy_events_old_revision_chk CHECK (
133
+ (old_revision_id IS NULL AND old_revision IS NULL AND old_policy_hash IS NULL)
134
+ OR (old_revision_id IS NOT NULL AND old_revision > 0 AND old_policy_hash ~ '^[0-9a-f]{64}$')
135
+ ),
136
+ CONSTRAINT slack_task_policy_events_new_revision_chk CHECK (
137
+ new_revision > 0 AND new_policy_hash ~ '^[0-9a-f]{64}$' AND activation_version > 0
138
+ ),
139
+ CONSTRAINT slack_task_policy_events_receipt_chk CHECK (request_fingerprint ~ '^[0-9a-f]{64}$'),
140
+ CONSTRAINT slack_task_policy_events_audit_chk CHECK (
141
+ length(btrim(actor_subject_id)) BETWEEN 1 AND 1024
142
+ AND length(btrim(reason)) BETWEEN 1 AND 4096
143
+ ),
144
+ CONSTRAINT slack_task_policy_events_workspace_operation_uq UNIQUE (workspace_id, operation_id),
145
+ CONSTRAINT slack_task_policy_events_workspace_version_uq UNIQUE (workspace_id, activation_version),
146
+ CONSTRAINT slack_task_policy_events_new_revision_fk
147
+ FOREIGN KEY (account_id, workspace_id, new_revision_id)
148
+ REFERENCES slack_task_policy_revisions(account_id, workspace_id, id) ON DELETE RESTRICT
149
+ );
150
+
151
+ CREATE INDEX slack_task_policy_events_workspace_time_idx
152
+ ON slack_task_policy_activation_events(workspace_id, created_at DESC, id);
153
+
154
+ CREATE TABLE slack_shared_task_origins (
155
+ interaction_id uuid PRIMARY KEY,
156
+ account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
157
+ workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
158
+ connection_id uuid NOT NULL REFERENCES connections(id) ON DELETE CASCADE,
159
+ session_id uuid NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
160
+ slack_team_id text NOT NULL,
161
+ source_channel_id text NOT NULL,
162
+ source_thread_ts text NOT NULL,
163
+ initiating_slack_user_id text NOT NULL,
164
+ policy_revision_id uuid NOT NULL,
165
+ policy_hash text NOT NULL,
166
+ policy_activation_version bigint NOT NULL,
167
+ publication_mode text NOT NULL,
168
+ created_at timestamptz NOT NULL DEFAULT now(),
169
+ CONSTRAINT slack_shared_task_origins_interaction_fk
170
+ FOREIGN KEY (account_id, workspace_id, interaction_id)
171
+ REFERENCES slack_interactions(account_id, workspace_id, id) ON DELETE CASCADE,
172
+ CONSTRAINT slack_shared_task_origins_policy_fk
173
+ FOREIGN KEY (account_id, workspace_id, policy_revision_id)
174
+ REFERENCES slack_task_policy_revisions(account_id, workspace_id, id) ON DELETE RESTRICT,
175
+ CONSTRAINT slack_shared_task_origins_identity_uq UNIQUE (workspace_id, session_id),
176
+ CONSTRAINT slack_shared_task_origins_bounds_chk CHECK (
177
+ length(slack_team_id) BETWEEN 1 AND 128
178
+ AND length(source_channel_id) BETWEEN 1 AND 128
179
+ AND length(source_thread_ts) BETWEEN 1 AND 64
180
+ AND length(initiating_slack_user_id) BETWEEN 1 AND 128
181
+ AND policy_hash ~ '^[0-9a-f]{64}$'
182
+ AND policy_activation_version > 0
183
+ AND publication_mode IN ('never', 'approval_required', 'allow')
184
+ )
185
+ );
186
+
187
+ CREATE OR REPLACE FUNCTION slack_shared_task_origin_forbid_mutation()
188
+ RETURNS trigger LANGUAGE plpgsql AS $$
189
+ BEGIN
190
+ RAISE EXCEPTION 'Slack shared task origin is immutable' USING ERRCODE = '42501';
191
+ END;
192
+ $$;
193
+
194
+ CREATE TRIGGER slack_shared_task_origins_immutable
195
+ BEFORE UPDATE OR DELETE ON slack_shared_task_origins
196
+ FOR EACH ROW EXECUTE FUNCTION slack_shared_task_origin_forbid_mutation();
197
+
198
+ ALTER TABLE slack_interaction_action_handles
199
+ DROP CONSTRAINT slack_interaction_action_handles_bounds_check;
200
+ ALTER TABLE slack_interaction_action_handles
201
+ ADD CONSTRAINT slack_interaction_action_handles_bounds_check CHECK (
202
+ session_event_sequence >= 0
203
+ AND octet_length(action_kind) BETWEEN 1 AND 64
204
+ AND octet_length(action_key) BETWEEN 1 AND 512
205
+ AND (target_id IS NULL OR octet_length(target_id) BETWEEN 1 AND 512)
206
+ AND (target_value IS NULL OR octet_length(target_value) BETWEEN 1 AND 1024)
207
+ AND octet_length(authorized_subject_id) BETWEEN 1 AND 512
208
+ AND octet_length(authorized_slack_user_id) BETWEEN 1 AND 64
209
+ AND action_kind IN (
210
+ 'approval_approve', 'approval_reject', 'human_input_select',
211
+ 'human_input_skip', 'session_status', 'session_pause', 'session_resume',
212
+ 'shared_result_publish'
213
+ )
214
+ AND status IN ('pending', 'completed', 'stale')
215
+ AND (result IS NULL OR octet_length(result) BETWEEN 1 AND 64)
216
+ AND (
217
+ (status = 'pending' AND result IS NULL AND completed_at IS NULL)
218
+ OR (status IN ('completed', 'stale') AND result IS NOT NULL AND completed_at IS NOT NULL)
219
+ )
220
+ ) NOT VALID;
221
+ ALTER TABLE slack_interaction_action_handles
222
+ VALIDATE CONSTRAINT slack_interaction_action_handles_bounds_check;
223
+
224
+ CREATE OR REPLACE FUNCTION slack_task_policy_forbid_mutation()
225
+ RETURNS trigger LANGUAGE plpgsql AS $$
226
+ BEGIN
227
+ RAISE EXCEPTION 'Slack task policy history is immutable' USING ERRCODE = '42501';
228
+ END;
229
+ $$;
230
+
231
+ CREATE TRIGGER slack_task_policy_revisions_immutable
232
+ BEFORE UPDATE OR DELETE ON slack_task_policy_revisions
233
+ FOR EACH ROW EXECUTE FUNCTION slack_task_policy_forbid_mutation();
234
+ CREATE TRIGGER slack_task_policy_events_immutable
235
+ BEFORE UPDATE OR DELETE ON slack_task_policy_activation_events
236
+ FOR EACH ROW EXECUTE FUNCTION slack_task_policy_forbid_mutation();
237
+
238
+ CREATE OR REPLACE FUNCTION slack_task_policy_guard_head_mutation()
239
+ RETURNS trigger LANGUAGE plpgsql AS $$
240
+ DECLARE expected_workspace_id text;
241
+ BEGIN
242
+ expected_workspace_id := current_setting('opengeni.slack_task_policy_lifecycle_workspace_id', true);
243
+ IF expected_workspace_id IS NULL OR expected_workspace_id = ''
244
+ OR expected_workspace_id IS DISTINCT FROM coalesce(NEW.workspace_id, OLD.workspace_id)::text
245
+ THEN
246
+ RAISE EXCEPTION 'Slack task policy head mutation is lifecycle-only' USING ERRCODE = '42501';
247
+ END IF;
248
+ IF TG_OP = 'DELETE' THEN RETURN OLD; END IF;
249
+ RETURN NEW;
250
+ END;
251
+ $$;
252
+
253
+ CREATE TRIGGER slack_task_policy_heads_lifecycle_only
254
+ BEFORE INSERT OR UPDATE OR DELETE ON slack_task_policy_heads
255
+ FOR EACH ROW EXECUTE FUNCTION slack_task_policy_guard_head_mutation();
256
+
257
+ CREATE OR REPLACE FUNCTION slack_task_policy_update(
258
+ p_operation_id uuid,
259
+ p_request_fingerprint text,
260
+ p_account_id uuid,
261
+ p_workspace_id uuid,
262
+ p_policy jsonb,
263
+ p_expected_current_revision_id uuid,
264
+ p_expected_activation_version bigint,
265
+ p_actor_subject_id text,
266
+ p_reason text
267
+ ) RETURNS TABLE (revision_id uuid, event_id uuid)
268
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
269
+ DECLARE
270
+ current_head slack_task_policy_heads%ROWTYPE;
271
+ existing_revision slack_task_policy_revisions%ROWTYPE;
272
+ existing_event slack_task_policy_activation_events%ROWTYPE;
273
+ created_revision slack_task_policy_revisions%ROWTYPE;
274
+ next_version bigint;
275
+ accepted_at timestamptz;
276
+ BEGIN
277
+ IF NULLIF(current_setting('opengeni.account_id', true), '')::uuid IS DISTINCT FROM p_account_id
278
+ OR NULLIF(current_setting('opengeni.workspace_id', true), '')::uuid IS DISTINCT FROM p_workspace_id
279
+ OR NULLIF(current_setting('opengeni.subject_id', true), '') IS DISTINCT FROM p_actor_subject_id
280
+ OR NULLIF(current_setting('opengeni.principal_kind', true), '') IS DISTINCT FROM 'human_session'
281
+ THEN
282
+ RAISE EXCEPTION 'Slack task policy update requires exact human workspace authority'
283
+ USING ERRCODE = '42501';
284
+ END IF;
285
+ IF p_request_fingerprint !~ '^[0-9a-f]{64}$'
286
+ OR p_expected_activation_version < 0
287
+ OR NOT slack_task_policy_valid(p_policy)
288
+ OR length(btrim(p_actor_subject_id)) NOT BETWEEN 1 AND 1024
289
+ OR length(btrim(p_reason)) NOT BETWEEN 1 AND 4096
290
+ THEN
291
+ RAISE EXCEPTION 'Slack task policy update input is invalid' USING ERRCODE = '22023';
292
+ END IF;
293
+
294
+ PERFORM 1 FROM workspaces
295
+ WHERE id = p_workspace_id AND account_id = p_account_id FOR UPDATE;
296
+ IF NOT FOUND THEN RAISE EXCEPTION 'workspace was not found' USING ERRCODE = '42501'; END IF;
297
+
298
+ SELECT * INTO existing_revision FROM slack_task_policy_revisions
299
+ WHERE workspace_id = p_workspace_id AND operation_id = p_operation_id;
300
+ SELECT * INTO existing_event FROM slack_task_policy_activation_events
301
+ WHERE workspace_id = p_workspace_id AND operation_id = p_operation_id;
302
+ IF existing_revision.id IS NOT NULL OR existing_event.id IS NOT NULL THEN
303
+ IF existing_revision.id IS NULL OR existing_event.id IS NULL
304
+ OR existing_revision.request_fingerprint IS DISTINCT FROM p_request_fingerprint
305
+ OR existing_event.request_fingerprint IS DISTINCT FROM p_request_fingerprint
306
+ THEN
307
+ RAISE EXCEPTION 'Slack task policy operation id was reused' USING ERRCODE = 'P1471';
308
+ END IF;
309
+ revision_id := existing_revision.id;
310
+ event_id := existing_event.id;
311
+ RETURN NEXT;
312
+ RETURN;
313
+ END IF;
314
+
315
+ SELECT * INTO current_head FROM slack_task_policy_heads
316
+ WHERE workspace_id = p_workspace_id FOR UPDATE;
317
+ IF coalesce(current_head.activation_version, 0) IS DISTINCT FROM p_expected_activation_version
318
+ OR current_head.revision_id IS DISTINCT FROM p_expected_current_revision_id
319
+ THEN
320
+ RAISE EXCEPTION 'Slack task policy active revision changed' USING ERRCODE = '40001';
321
+ END IF;
322
+
323
+ accepted_at := clock_timestamp();
324
+ INSERT INTO slack_task_policy_revisions (
325
+ operation_id, request_fingerprint, account_id, workspace_id, policy, policy_hash,
326
+ supersedes_revision_id, created_by_subject_id, created_at
327
+ ) VALUES (
328
+ p_operation_id, p_request_fingerprint, p_account_id, p_workspace_id, p_policy,
329
+ slack_task_policy_hash(p_policy), current_head.revision_id, p_actor_subject_id, accepted_at
330
+ ) RETURNING * INTO created_revision;
331
+
332
+ next_version := coalesce(current_head.activation_version, 0) + 1;
333
+ PERFORM set_config('opengeni.slack_task_policy_lifecycle_workspace_id', p_workspace_id::text, true);
334
+ INSERT INTO slack_task_policy_heads (
335
+ account_id, workspace_id, revision_id, revision, policy_hash, activation_version, activated_at
336
+ ) VALUES (
337
+ p_account_id, p_workspace_id, created_revision.id, created_revision.revision,
338
+ created_revision.policy_hash, next_version, accepted_at
339
+ ) ON CONFLICT (workspace_id) DO UPDATE SET
340
+ revision_id = excluded.revision_id,
341
+ revision = excluded.revision,
342
+ policy_hash = excluded.policy_hash,
343
+ activation_version = excluded.activation_version,
344
+ activated_at = excluded.activated_at;
345
+ PERFORM set_config('opengeni.slack_task_policy_lifecycle_workspace_id', '', true);
346
+
347
+ INSERT INTO slack_task_policy_activation_events (
348
+ operation_id, request_fingerprint, account_id, workspace_id, activation_version,
349
+ old_revision_id, old_revision, old_policy_hash,
350
+ new_revision_id, new_revision, new_policy_hash,
351
+ actor_subject_id, reason, created_at
352
+ ) VALUES (
353
+ p_operation_id, p_request_fingerprint, p_account_id, p_workspace_id, next_version,
354
+ current_head.revision_id, current_head.revision, current_head.policy_hash,
355
+ created_revision.id, created_revision.revision, created_revision.policy_hash,
356
+ p_actor_subject_id, p_reason, accepted_at
357
+ ) RETURNING id INTO event_id;
358
+ revision_id := created_revision.id;
359
+ RETURN NEXT;
360
+ END;
361
+ $$;
362
+
363
+ REVOKE ALL ON FUNCTION slack_task_policy_update(uuid,text,uuid,uuid,jsonb,uuid,bigint,text,text) FROM PUBLIC;
364
+
365
+ ALTER TABLE slack_task_policy_revisions ENABLE ROW LEVEL SECURITY;
366
+ ALTER TABLE slack_task_policy_revisions FORCE ROW LEVEL SECURITY;
367
+ ALTER TABLE slack_task_policy_heads ENABLE ROW LEVEL SECURITY;
368
+ ALTER TABLE slack_task_policy_heads FORCE ROW LEVEL SECURITY;
369
+ ALTER TABLE slack_task_policy_activation_events ENABLE ROW LEVEL SECURITY;
370
+ ALTER TABLE slack_task_policy_activation_events FORCE ROW LEVEL SECURITY;
371
+ ALTER TABLE slack_shared_task_origins ENABLE ROW LEVEL SECURITY;
372
+ ALTER TABLE slack_shared_task_origins FORCE ROW LEVEL SECURITY;
373
+
374
+ CREATE POLICY workspace_isolation ON slack_task_policy_revisions
375
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id));
376
+ CREATE POLICY workspace_isolation ON slack_task_policy_heads
377
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id));
378
+ CREATE POLICY workspace_isolation ON slack_task_policy_activation_events
379
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id));
380
+ CREATE POLICY workspace_isolation ON slack_shared_task_origins
381
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
382
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
383
+
384
+ REVOKE ALL ON TABLE slack_task_policy_revisions FROM PUBLIC;
385
+ REVOKE ALL ON TABLE slack_task_policy_heads FROM PUBLIC;
386
+ REVOKE ALL ON TABLE slack_task_policy_activation_events FROM PUBLIC;
387
+ REVOKE ALL ON TABLE slack_shared_task_origins FROM PUBLIC;
388
+ REVOKE ALL ON SEQUENCE slack_task_policy_revision_seq FROM PUBLIC;
389
+
390
+ DO $grants$
391
+ BEGIN
392
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
393
+ GRANT SELECT ON TABLE slack_task_policy_revisions TO opengeni_app;
394
+ GRANT SELECT ON TABLE slack_task_policy_heads TO opengeni_app;
395
+ GRANT SELECT ON TABLE slack_task_policy_activation_events TO opengeni_app;
396
+ GRANT SELECT, INSERT ON TABLE slack_shared_task_origins TO opengeni_app;
397
+ GRANT EXECUTE ON FUNCTION slack_task_policy_update(uuid,text,uuid,uuid,jsonb,uuid,bigint,text,text)
398
+ TO opengeni_app;
399
+ END IF;
400
+ END $grants$;
@@ -0,0 +1,10 @@
1
+ -- deployment-mode: rolling
2
+ -- Preserve the signed Slack event's bounded file-presence fact through the
3
+ -- durable inbox so mixed-text DMs and existing-thread replies fetch only their
4
+ -- exact provider message before importing authorized images.
5
+
6
+ SET LOCAL lock_timeout = '5s';
7
+ SET LOCAL statement_timeout = '10min';
8
+
9
+ ALTER TABLE "slack_interaction_inbox"
10
+ ADD COLUMN "has_files" boolean NOT NULL DEFAULT false;
@@ -0,0 +1,84 @@
1
+ -- deployment-mode: rolling
2
+ -- Inert authority identity foundation for Variable Sets and Rigs. This slice
3
+ -- changes no runtime authorization, RLS policy, lifecycle GUC, API/DAO path,
4
+ -- runnable snapshot, grant consumption, or worker materialization behavior.
5
+
6
+ SET LOCAL lock_timeout = '5s';
7
+ SET LOCAL statement_timeout = '10min';
8
+
9
+ ALTER TABLE "workspace_variable_sets"
10
+ ADD COLUMN "authority_scope" text NOT NULL DEFAULT 'workspace',
11
+ ADD COLUMN "authority_id" uuid,
12
+ ADD COLUMN "owner_organization_membership_id" uuid,
13
+ ADD COLUMN "origin_workspace_id" uuid;
14
+
15
+ ALTER TABLE "rigs"
16
+ ADD COLUMN "authority_scope" text NOT NULL DEFAULT 'workspace',
17
+ ADD COLUMN "authority_id" uuid,
18
+ ADD COLUMN "owner_organization_membership_id" uuid,
19
+ ADD COLUMN "origin_workspace_id" uuid;
20
+
21
+ ALTER TABLE "workspace_variable_sets"
22
+ ADD CONSTRAINT "workspace_variable_sets_authority_scope_check"
23
+ CHECK ("authority_scope" IN ('workspace', 'user')) NOT VALID,
24
+ ADD CONSTRAINT "workspace_variable_sets_authority_shape_check" CHECK (
25
+ (
26
+ "authority_scope" = 'workspace'
27
+ AND "authority_id" IS NULL
28
+ AND "owner_organization_membership_id" IS NULL
29
+ ) OR (
30
+ "authority_scope" = 'user'
31
+ AND "authority_id" IS NOT NULL
32
+ AND "owner_organization_membership_id" IS NOT NULL
33
+ )
34
+ ) NOT VALID,
35
+ ADD CONSTRAINT "workspace_variable_sets_authority_fk" FOREIGN KEY (
36
+ "authority_id", "account_id", "owner_organization_membership_id"
37
+ ) REFERENCES "organization_user_resource_authorities"(
38
+ "id", "account_id", "organization_membership_id"
39
+ ) ON DELETE RESTRICT NOT VALID,
40
+ ADD CONSTRAINT "workspace_variable_sets_origin_workspace_fk" FOREIGN KEY (
41
+ "origin_workspace_id", "account_id"
42
+ ) REFERENCES "workspaces"("id", "account_id")
43
+ ON DELETE SET NULL ("origin_workspace_id") NOT VALID;
44
+
45
+ ALTER TABLE "rigs"
46
+ ADD CONSTRAINT "rigs_authority_scope_check"
47
+ CHECK ("authority_scope" IN ('workspace', 'user')) NOT VALID,
48
+ ADD CONSTRAINT "rigs_authority_shape_check" CHECK (
49
+ (
50
+ "authority_scope" = 'workspace'
51
+ AND "authority_id" IS NULL
52
+ AND "owner_organization_membership_id" IS NULL
53
+ ) OR (
54
+ "authority_scope" = 'user'
55
+ AND "authority_id" IS NOT NULL
56
+ AND "owner_organization_membership_id" IS NOT NULL
57
+ )
58
+ ) NOT VALID,
59
+ ADD CONSTRAINT "rigs_authority_fk" FOREIGN KEY (
60
+ "authority_id", "account_id", "owner_organization_membership_id"
61
+ ) REFERENCES "organization_user_resource_authorities"(
62
+ "id", "account_id", "organization_membership_id"
63
+ ) ON DELETE RESTRICT NOT VALID,
64
+ ADD CONSTRAINT "rigs_origin_workspace_fk" FOREIGN KEY (
65
+ "origin_workspace_id", "account_id"
66
+ ) REFERENCES "workspaces"("id", "account_id")
67
+ ON DELETE SET NULL ("origin_workspace_id") NOT VALID;
68
+
69
+ ALTER TABLE "workspace_variable_sets"
70
+ VALIDATE CONSTRAINT "workspace_variable_sets_authority_scope_check",
71
+ VALIDATE CONSTRAINT "workspace_variable_sets_authority_shape_check",
72
+ VALIDATE CONSTRAINT "workspace_variable_sets_authority_fk",
73
+ VALIDATE CONSTRAINT "workspace_variable_sets_origin_workspace_fk";
74
+
75
+ ALTER TABLE "rigs"
76
+ VALIDATE CONSTRAINT "rigs_authority_scope_check",
77
+ VALIDATE CONSTRAINT "rigs_authority_shape_check",
78
+ VALIDATE CONSTRAINT "rigs_authority_fk",
79
+ VALIDATE CONSTRAINT "rigs_origin_workspace_fk";
80
+
81
+ COMMENT ON COLUMN "workspace_variable_sets"."authority_scope" IS
82
+ 'Inert authority identity foundation. Runtime access remains workspace-scoped until a later activation migration.';
83
+ COMMENT ON COLUMN "rigs"."authority_scope" IS
84
+ 'Inert authority identity foundation. Runtime access remains workspace-scoped until a later activation migration.';