@opengeni/db 3.7.4 → 3.8.2-canary.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 (77) hide show
  1. package/dist/automations.d.ts +4 -0
  2. package/dist/canonical-human-identities.js +3 -3
  3. package/dist/{chunk-O56DM5GZ.js → chunk-53A2PEHT.js} +328 -31
  4. package/dist/chunk-53A2PEHT.js.map +1 -0
  5. package/dist/{chunk-SZAWDXQC.js → chunk-5S4BQAHX.js} +3 -3
  6. package/dist/{chunk-S3TDDCS2.js → chunk-CITYUGMW.js} +3 -3
  7. package/dist/{chunk-4NAR7FBA.js → chunk-F5YOZEYQ.js} +49 -1
  8. package/dist/chunk-F5YOZEYQ.js.map +1 -0
  9. package/dist/{chunk-F6PBS6WE.js → chunk-HZV7O6JR.js} +2 -2
  10. package/dist/{chunk-5NNCGFQY.js → chunk-RLQVPOVR.js} +2 -2
  11. package/dist/{chunk-DZCYW2WT.js → chunk-UKOGHUGC.js} +2 -2
  12. package/dist/{chunk-US4IS6IV.js → chunk-X6TO5HII.js} +2 -2
  13. package/dist/{chunk-U4WPPEBH.js → chunk-Y53WEFYB.js} +2 -2
  14. package/dist/connection-token-resolver.d.ts +4 -0
  15. package/dist/editable-artifact-durable-export.js +2 -2
  16. package/dist/editable-artifacts.js +3 -3
  17. package/dist/index.d.ts +164 -14
  18. package/dist/index.js +7327 -5442
  19. package/dist/index.js.map +1 -1
  20. package/dist/insights.d.ts +9 -0
  21. package/dist/managed-auth-session-sets.js +3 -3
  22. package/dist/model-catalog.d.ts +153 -0
  23. package/dist/new-session-drafts.d.ts +12 -0
  24. package/dist/organization-membership-lifecycle.d.ts +8 -1
  25. package/dist/organization-model-providers.d.ts +101 -0
  26. package/dist/pack-components.d.ts +1 -0
  27. package/dist/pr-review.d.ts +9 -0
  28. package/dist/provision-roles.js +1 -1
  29. package/dist/runtime-posture.d.ts +7 -7
  30. package/dist/schema.d.ts +989 -2
  31. package/dist/schema.js +11 -1
  32. package/dist/session-background-commands.js +3 -3
  33. package/dist/session-control.d.ts +35 -0
  34. package/dist/session-queue-commands.d.ts +17 -0
  35. package/dist/session-tenancy.js +3 -3
  36. package/dist/video-generation.js +3 -3
  37. package/dist/xai-subscription.d.ts +8 -0
  38. package/drizzle/0387_bound_host_export_session_payloads.sql +165 -0
  39. package/drizzle/0388_sandbox_provider_deadline_interactions.sql +491 -0
  40. package/drizzle/0389_model_catalog_and_gateway_custom_models.sql +241 -0
  41. package/drizzle/0390_organization_model_provider_connections.sql +197 -0
  42. package/drizzle/0391_sandbox_provider_deadline_interaction_followup.sql +295 -0
  43. package/drizzle/0392_context_compaction_pending_observability.sql +198 -0
  44. package/drizzle/0393_workspace_memory_and_learning_defaults.sql +102 -0
  45. package/drizzle/0394_session_selected_skill_activation.sql +20 -0
  46. package/drizzle/0395_scheduled_task_unclaimed_occurrence_invalidation.sql +154 -0
  47. package/drizzle/0396_model_call_equivalent_credit_cost.sql +28 -0
  48. package/drizzle/0397_sandbox_deadline_rotation_preemption.sql +113 -0
  49. package/drizzle/0398_organization_workspace_management_entry.sql +206 -0
  50. package/drizzle/0399_additional_managed_organization_creation.sql +691 -0
  51. package/package.json +6 -6
  52. package/src/automations.ts +9 -1
  53. package/src/connection-token-resolver.ts +12 -0
  54. package/src/index.ts +1551 -272
  55. package/src/insights-model-bundle.ts +54 -5
  56. package/src/insights.ts +53 -7
  57. package/src/model-catalog.ts +682 -0
  58. package/src/new-session-drafts.ts +66 -21
  59. package/src/organization-membership-lifecycle.ts +47 -5
  60. package/src/organization-model-providers.ts +614 -0
  61. package/src/pack-components.ts +15 -2
  62. package/src/pr-review.ts +19 -0
  63. package/src/provision-roles.ts +2 -0
  64. package/src/runtime-posture.ts +48 -0
  65. package/src/schema.ts +348 -32
  66. package/src/session-control.ts +170 -0
  67. package/src/session-queue-commands.ts +157 -82
  68. package/src/xai-subscription.ts +66 -14
  69. package/dist/chunk-4NAR7FBA.js.map +0 -1
  70. package/dist/chunk-O56DM5GZ.js.map +0 -1
  71. /package/dist/{chunk-SZAWDXQC.js.map → chunk-5S4BQAHX.js.map} +0 -0
  72. /package/dist/{chunk-S3TDDCS2.js.map → chunk-CITYUGMW.js.map} +0 -0
  73. /package/dist/{chunk-F6PBS6WE.js.map → chunk-HZV7O6JR.js.map} +0 -0
  74. /package/dist/{chunk-5NNCGFQY.js.map → chunk-RLQVPOVR.js.map} +0 -0
  75. /package/dist/{chunk-DZCYW2WT.js.map → chunk-UKOGHUGC.js.map} +0 -0
  76. /package/dist/{chunk-US4IS6IV.js.map → chunk-X6TO5HII.js.map} +0 -0
  77. /package/dist/{chunk-U4WPPEBH.js.map → chunk-Y53WEFYB.js.map} +0 -0
@@ -0,0 +1,295 @@
1
+ -- deployment-mode: rolling
2
+ -- Repair the post-0388 provider-deadline interaction lifecycle without
3
+ -- widening ordinary interaction expiry or weakening the session-tenancy fence.
4
+ SET LOCAL lock_timeout = '5s';
5
+ SET LOCAL statement_timeout = '10min';
6
+
7
+ -- The TTL-aware fence inventory below must discover due lease-free controller
8
+ -- transitions before the SECURITY DEFINER reaper opens its fenced owner scope.
9
+ -- Keep this as inventory-only SELECT authority; the 0388 fenced owner policies
10
+ -- remain the sole mutation path after every workspace advisory lock is held.
11
+ DO $interaction_reaper_inventory_policies$
12
+ DECLARE
13
+ target_schema text := pg_catalog.current_schema();
14
+ target_schema_oid oid := pg_catalog.current_schema()::pg_catalog.regnamespace;
15
+ migration_owner text := current_user;
16
+ table_name text;
17
+ BEGIN
18
+ FOREACH table_name IN ARRAY ARRAY[
19
+ 'browser_sessions',
20
+ 'computer_sessions',
21
+ 'interaction_operations'
22
+ ] LOOP
23
+ EXECUTE pg_catalog.format(
24
+ 'DROP POLICY IF EXISTS session_tenancy_fence_inventory_read ON %I.%I',
25
+ target_schema,
26
+ table_name
27
+ );
28
+ EXECUTE pg_catalog.format(
29
+ 'CREATE POLICY session_tenancy_fence_inventory_read ON %I.%I '
30
+ || 'FOR SELECT USING ('
31
+ || '%I.session_tenancy_fence_owner_policy_active('
32
+ || 'current_user::text, %L::text, %s::oid, workspace_id, true))',
33
+ target_schema,
34
+ table_name,
35
+ target_schema,
36
+ migration_owner,
37
+ target_schema_oid
38
+ );
39
+ END LOOP;
40
+ END
41
+ $interaction_reaper_inventory_policies$;
42
+
43
+ -- The no-argument helper remains installed for reap_sandbox_leases() and old
44
+ -- in-flight callers. The interaction reaper uses this overload so its bounded
45
+ -- stale-operation batch can also see lease-free Connected Machine and attached-
46
+ -- device workspaces. Because SKIP LOCKED can advance beyond any preselected
47
+ -- operation row, fence every currently due workspace, but no healthy lease-free
48
+ -- interaction workspace, in canonical UUID order before taking row locks.
49
+ CREATE OR REPLACE FUNCTION acquire_sandbox_reaper_session_tenancy_fences(
50
+ p_interaction_holder_ttl_ms bigint
51
+ )
52
+ RETURNS integer
53
+ LANGUAGE plpgsql
54
+ SECURITY DEFINER
55
+ SET search_path FROM CURRENT
56
+ AS $interaction_reaper_fences$
57
+ DECLARE
58
+ inventory_capability_id uuid;
59
+ workspace_id_value uuid;
60
+ locked_count integer := 0;
61
+ BEGIN
62
+ IF p_interaction_holder_ttl_ms <= 0 THEN
63
+ RETURN 0;
64
+ END IF;
65
+ PERFORM pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
66
+ inventory_capability_id :=
67
+ opengeni_private.open_session_tenancy_fence_inventory(
68
+ session_tenancy_fence_target_schema()
69
+ );
70
+ FOR workspace_id_value IN
71
+ SELECT candidate.workspace_id
72
+ FROM (
73
+ SELECT lease.workspace_id
74
+ FROM sandbox_leases lease
75
+ WHERE lease.workspace_id IS NOT NULL
76
+ AND (
77
+ lease.liveness <> 'cold'
78
+ OR EXISTS (
79
+ SELECT 1
80
+ FROM sandbox_lease_holders holder
81
+ WHERE holder.lease_id = lease.id
82
+ )
83
+ )
84
+ UNION
85
+ SELECT operation.workspace_id
86
+ FROM interaction_operations operation
87
+ WHERE operation.workspace_id IS NOT NULL
88
+ -- Lease-free prepared operations remain replayable. A dispatched
89
+ -- operation is the first controller-side effect and is the only
90
+ -- lease-free transition the ordinary stale-transition reaper settles.
91
+ AND operation.state = 'dispatched'
92
+ AND operation.updated_at
93
+ < pg_catalog.now()
94
+ - pg_catalog.make_interval(
95
+ secs => p_interaction_holder_ttl_ms / 1000.0
96
+ )
97
+ AND (
98
+ EXISTS (
99
+ SELECT 1
100
+ FROM browser_sessions browser
101
+ WHERE operation.resource_kind = 'browser_session'
102
+ AND operation.account_id = browser.account_id
103
+ AND operation.workspace_id = browser.workspace_id
104
+ AND operation.resource_id = browser.id
105
+ AND browser.lifecycle IN (
106
+ 'starting', 'suspending', 'restoring', 'ending'
107
+ )
108
+ )
109
+ OR EXISTS (
110
+ SELECT 1
111
+ FROM computer_sessions computer
112
+ WHERE operation.resource_kind = 'computer_session'
113
+ AND operation.account_id = computer.account_id
114
+ AND operation.workspace_id = computer.workspace_id
115
+ AND operation.resource_id = computer.id
116
+ AND computer.lifecycle IN ('starting', 'ending')
117
+ )
118
+ )
119
+ ) candidate
120
+ ORDER BY candidate.workspace_id
121
+ LOOP
122
+ PERFORM acquire_session_tenancy_fence(workspace_id_value);
123
+ locked_count := locked_count + 1;
124
+ END LOOP;
125
+ PERFORM opengeni_private.close_session_tenancy_fence_inventory(
126
+ inventory_capability_id
127
+ );
128
+ RETURN locked_count;
129
+ EXCEPTION WHEN OTHERS THEN
130
+ PERFORM opengeni_private.close_session_tenancy_fence_inventory(
131
+ inventory_capability_id
132
+ );
133
+ RAISE;
134
+ END
135
+ $interaction_reaper_fences$;
136
+
137
+ REVOKE ALL ON FUNCTION
138
+ acquire_sandbox_reaper_session_tenancy_fences(bigint)
139
+ FROM PUBLIC;
140
+
141
+ -- CREATE OR REPLACE records `SET search_path FROM CURRENT` from the migrator
142
+ -- connection. Re-pin this overload to the exact target schema before callers
143
+ -- can invoke the enclosing reaper, so a caller-created temp relation cannot
144
+ -- shadow its lease-free inventory before workspace fences are acquired.
145
+ DO $pin_interaction_reaper_fence_helper$
146
+ DECLARE
147
+ target_schema text := pg_catalog.current_schema();
148
+ BEGIN
149
+ EXECUTE pg_catalog.format(
150
+ 'ALTER FUNCTION %I.acquire_sandbox_reaper_session_tenancy_fences(bigint) '
151
+ || 'SET search_path = pg_catalog, %I, pg_temp',
152
+ target_schema,
153
+ target_schema
154
+ );
155
+ END
156
+ $pin_interaction_reaper_fence_helper$;
157
+
158
+ -- Patch only the exact 0388 definition. The interaction reaper keeps the
159
+ -- canonical advisory-fence -> owner capability -> operation -> lease -> holder
160
+ -- ordering. The holder EXISTS is an unlocked eligibility read before LIMIT;
161
+ -- lease rows are still the first ownership rows locked by the deadline phase.
162
+ DO $provider_deadline_interaction_followup$
163
+ DECLARE
164
+ data_schema text := pg_catalog.current_schema();
165
+ definition text;
166
+ patched text;
167
+ fence_anchor constant text :=
168
+ E' PERFORM acquire_sandbox_reaper_session_tenancy_fences();';
169
+ fence_replacement constant text :=
170
+ E' -- 0391 provider-deadline interaction follow-up: inventory due\n'
171
+ || E' -- lease-free transitions before any interaction row lock.\n'
172
+ || E' PERFORM acquire_sandbox_reaper_session_tenancy_fences(\n'
173
+ || E' p_interaction_holder_ttl_ms\n'
174
+ || E' );';
175
+ liveness_anchor constant text :=
176
+ E' AND lease.liveness IN (''warming'', ''warm'')';
177
+ liveness_replacement constant text :=
178
+ E' AND lease.liveness IN (''warming'', ''warm'', ''draining'')';
179
+ deadline_anchor constant text :=
180
+ E' AND lease.provider_deadline_at <= pg_catalog.now()';
181
+ deadline_replacement text;
182
+ occurrences integer;
183
+ BEGIN
184
+ SELECT pg_catalog.pg_get_functiondef(
185
+ 'opengeni_private.reap_stale_interaction_transitions(bigint)'::regprocedure
186
+ ) INTO definition;
187
+
188
+ IF definition IS NULL
189
+ OR pg_catalog.strpos(definition, 'deadline_lease_ids uuid[]') = 0
190
+ OR pg_catalog.strpos(definition, 'provider_deadline_rotation') = 0
191
+ OR pg_catalog.strpos(definition, 'open_session_tenancy_fenced_access') = 0
192
+ OR pg_catalog.strpos(definition, 'LIMIT 500') = 0
193
+ THEN
194
+ RAISE EXCEPTION '0391 interaction reaper prerequisite definition drift'
195
+ USING ERRCODE = '55000';
196
+ END IF;
197
+
198
+ IF pg_catalog.strpos(
199
+ definition,
200
+ '0391 provider-deadline interaction follow-up'
201
+ ) > 0 THEN
202
+ IF pg_catalog.strpos(
203
+ definition,
204
+ 'acquire_sandbox_reaper_session_tenancy_fences('
205
+ ) = 0
206
+ OR pg_catalog.strpos(
207
+ definition,
208
+ 'p_interaction_holder_ttl_ms'
209
+ ) = 0
210
+ OR pg_catalog.strpos(
211
+ definition,
212
+ 'lease.liveness IN (''warming'', ''warm'', ''draining'')'
213
+ ) = 0
214
+ OR pg_catalog.strpos(
215
+ definition,
216
+ 'interaction_holder.kind = ''interaction'''
217
+ ) = 0
218
+ THEN
219
+ RAISE EXCEPTION '0391 interaction reaper replay definition drift'
220
+ USING ERRCODE = '55000';
221
+ END IF;
222
+ ELSE
223
+ patched := definition;
224
+
225
+ occurrences := (
226
+ pg_catalog.length(patched)
227
+ - pg_catalog.length(pg_catalog.replace(patched, fence_anchor, ''))
228
+ ) / pg_catalog.length(fence_anchor);
229
+ IF occurrences <> 1 THEN
230
+ RAISE EXCEPTION '0391 interaction reaper fence anchor drift'
231
+ USING ERRCODE = '55000';
232
+ END IF;
233
+ patched := pg_catalog.replace(patched, fence_anchor, fence_replacement);
234
+
235
+ occurrences := (
236
+ pg_catalog.length(patched)
237
+ - pg_catalog.length(pg_catalog.replace(patched, liveness_anchor, ''))
238
+ ) / pg_catalog.length(liveness_anchor);
239
+ IF occurrences <> 1 THEN
240
+ RAISE EXCEPTION '0391 interaction reaper liveness anchor drift'
241
+ USING ERRCODE = '55000';
242
+ END IF;
243
+ patched := pg_catalog.replace(
244
+ patched,
245
+ liveness_anchor,
246
+ liveness_replacement
247
+ );
248
+
249
+ occurrences := (
250
+ pg_catalog.length(patched)
251
+ - pg_catalog.length(pg_catalog.replace(patched, deadline_anchor, ''))
252
+ ) / pg_catalog.length(deadline_anchor);
253
+ IF occurrences <> 1 THEN
254
+ RAISE EXCEPTION '0391 interaction reaper deadline anchor drift'
255
+ USING ERRCODE = '55000';
256
+ END IF;
257
+ deadline_replacement := deadline_anchor || pg_catalog.format(
258
+ E'\n AND EXISTS (\n'
259
+ || E' SELECT 1\n'
260
+ || E' FROM %I.sandbox_lease_holders interaction_holder\n'
261
+ || E' WHERE interaction_holder.lease_id = lease.id\n'
262
+ || E' AND interaction_holder.kind = ''interaction''\n'
263
+ || E' )',
264
+ data_schema
265
+ );
266
+ patched := pg_catalog.replace(
267
+ patched,
268
+ deadline_anchor,
269
+ deadline_replacement
270
+ );
271
+
272
+ IF pg_catalog.strpos(
273
+ patched,
274
+ '0391 provider-deadline interaction follow-up'
275
+ ) = 0
276
+ OR pg_catalog.strpos(
277
+ patched,
278
+ 'lease.liveness IN (''warming'', ''warm'', ''draining'')'
279
+ ) = 0
280
+ OR pg_catalog.strpos(
281
+ patched,
282
+ 'interaction_holder.kind = ''interaction'''
283
+ ) = 0
284
+ THEN
285
+ RAISE EXCEPTION '0391 interaction reaper patch failed'
286
+ USING ERRCODE = '55000';
287
+ END IF;
288
+
289
+ EXECUTE patched;
290
+ END IF;
291
+ END
292
+ $provider_deadline_interaction_followup$;
293
+
294
+ RESET statement_timeout;
295
+ RESET lock_timeout;
@@ -0,0 +1,198 @@
1
+ -- deployment-mode: rolling
2
+ -- Project exact-attempt automatic compaction starts into one content-free,
3
+ -- restart-safe aggregate. Prometheus labels must never carry tenant/session ids,
4
+ -- while process-local start/completion timestamps cannot correlate concurrent
5
+ -- activities or survive the worker whose provider call became stuck.
6
+
7
+ CREATE TABLE opengeni_private.context_compaction_pending_observations (
8
+ workspace_id uuid NOT NULL,
9
+ attempt_id uuid NOT NULL,
10
+ started_at timestamp with time zone NOT NULL,
11
+ PRIMARY KEY (workspace_id, attempt_id),
12
+ CONSTRAINT context_compaction_pending_observations_attempt_fk
13
+ FOREIGN KEY (workspace_id, attempt_id)
14
+ REFERENCES session_turn_attempts (workspace_id, id)
15
+ ON DELETE CASCADE
16
+ );
17
+
18
+ REVOKE ALL ON TABLE opengeni_private.context_compaction_pending_observations FROM PUBLIC;
19
+
20
+ CREATE OR REPLACE FUNCTION opengeni_private.project_context_compaction_event()
21
+ RETURNS trigger
22
+ LANGUAGE plpgsql
23
+ SECURITY DEFINER
24
+ SET search_path = pg_catalog
25
+ AS $project_context_compaction_event$
26
+ BEGIN
27
+ IF NEW.turn_attempt_id IS NULL THEN
28
+ RETURN NEW;
29
+ END IF;
30
+
31
+ IF NEW.type = 'session.context.compaction.started'
32
+ AND NEW.payload ->> 'trigger' = 'auto'
33
+ THEN
34
+ INSERT INTO opengeni_private.context_compaction_pending_observations (
35
+ workspace_id,
36
+ attempt_id,
37
+ started_at
38
+ ) VALUES (
39
+ NEW.workspace_id,
40
+ NEW.turn_attempt_id,
41
+ NEW.occurred_at
42
+ )
43
+ ON CONFLICT (workspace_id, attempt_id) DO UPDATE
44
+ SET started_at = EXCLUDED.started_at;
45
+ ELSIF NEW.type IN (
46
+ 'session.context.compacted',
47
+ 'session.context.compaction.skipped'
48
+ ) THEN
49
+ DELETE FROM opengeni_private.context_compaction_pending_observations pending
50
+ WHERE pending.workspace_id = NEW.workspace_id
51
+ AND pending.attempt_id = NEW.turn_attempt_id;
52
+ END IF;
53
+
54
+ RETURN NEW;
55
+ END
56
+ $project_context_compaction_event$;
57
+
58
+ REVOKE ALL ON FUNCTION opengeni_private.project_context_compaction_event() FROM PUBLIC;
59
+
60
+ CREATE TRIGGER session_events_project_context_compaction_pending
61
+ AFTER INSERT ON session_events
62
+ FOR EACH ROW
63
+ WHEN (
64
+ NEW.type IN (
65
+ 'session.context.compaction.started',
66
+ 'session.context.compacted',
67
+ 'session.context.compaction.skipped'
68
+ )
69
+ )
70
+ EXECUTE FUNCTION opengeni_private.project_context_compaction_event();
71
+
72
+ CREATE OR REPLACE FUNCTION opengeni_private.clear_context_compaction_pending_attempt()
73
+ RETURNS trigger
74
+ LANGUAGE plpgsql
75
+ SECURITY DEFINER
76
+ SET search_path = pg_catalog
77
+ AS $clear_context_compaction_pending_attempt$
78
+ BEGIN
79
+ IF OLD.state <> 'closed' AND NEW.state = 'closed' THEN
80
+ DELETE FROM opengeni_private.context_compaction_pending_observations pending
81
+ WHERE pending.workspace_id = NEW.workspace_id
82
+ AND pending.attempt_id = NEW.id;
83
+ END IF;
84
+ RETURN NEW;
85
+ END
86
+ $clear_context_compaction_pending_attempt$;
87
+
88
+ REVOKE ALL ON FUNCTION opengeni_private.clear_context_compaction_pending_attempt() FROM PUBLIC;
89
+
90
+ CREATE TRIGGER session_turn_attempts_clear_context_compaction_pending
91
+ AFTER UPDATE OF state ON session_turn_attempts
92
+ FOR EACH ROW
93
+ WHEN (OLD.state IS DISTINCT FROM NEW.state)
94
+ EXECUTE FUNCTION opengeni_private.clear_context_compaction_pending_attempt();
95
+
96
+ CREATE OR REPLACE FUNCTION opengeni_private.clear_context_compaction_pending_turn()
97
+ RETURNS trigger
98
+ LANGUAGE plpgsql
99
+ SECURITY DEFINER
100
+ SET search_path = pg_catalog
101
+ AS $clear_context_compaction_pending_turn$
102
+ BEGIN
103
+ IF OLD.active_attempt_id IS NOT NULL
104
+ AND OLD.active_attempt_id IS DISTINCT FROM NEW.active_attempt_id
105
+ THEN
106
+ DELETE FROM opengeni_private.context_compaction_pending_observations pending
107
+ WHERE pending.workspace_id = NEW.workspace_id
108
+ AND pending.attempt_id = OLD.active_attempt_id;
109
+ END IF;
110
+ RETURN NEW;
111
+ END
112
+ $clear_context_compaction_pending_turn$;
113
+
114
+ REVOKE ALL ON FUNCTION opengeni_private.clear_context_compaction_pending_turn() FROM PUBLIC;
115
+
116
+ CREATE TRIGGER session_turns_clear_context_compaction_pending
117
+ AFTER UPDATE OF active_attempt_id ON session_turns
118
+ FOR EACH ROW
119
+ WHEN (OLD.active_attempt_id IS DISTINCT FROM NEW.active_attempt_id)
120
+ EXECUTE FUNCTION opengeni_private.clear_context_compaction_pending_turn();
121
+
122
+ -- The migration owner is bound by FORCE RLS in production. Open the standard
123
+ -- owner-only posture window while reconstructing any automatic provider call
124
+ -- already in flight when this rolling migration lands.
125
+ ALTER TABLE session_events NO FORCE ROW LEVEL SECURITY;
126
+ ALTER TABLE session_turn_attempts NO FORCE ROW LEVEL SECURITY;
127
+ ALTER TABLE session_turns NO FORCE ROW LEVEL SECURITY;
128
+
129
+ WITH latest_landmark AS (
130
+ SELECT DISTINCT ON (event.workspace_id, event.turn_attempt_id)
131
+ event.workspace_id,
132
+ event.turn_attempt_id,
133
+ event.type,
134
+ event.payload,
135
+ event.occurred_at
136
+ FROM session_events event
137
+ WHERE event.turn_attempt_id IS NOT NULL
138
+ AND event.type IN (
139
+ 'session.context.compaction.started',
140
+ 'session.context.compacted',
141
+ 'session.context.compaction.skipped'
142
+ )
143
+ ORDER BY
144
+ event.workspace_id,
145
+ event.turn_attempt_id,
146
+ event.sequence DESC,
147
+ event.id DESC
148
+ )
149
+ INSERT INTO opengeni_private.context_compaction_pending_observations (
150
+ workspace_id,
151
+ attempt_id,
152
+ started_at
153
+ )
154
+ SELECT
155
+ landmark.workspace_id,
156
+ landmark.turn_attempt_id,
157
+ landmark.occurred_at
158
+ FROM latest_landmark landmark
159
+ JOIN session_turn_attempts attempt
160
+ ON attempt.workspace_id = landmark.workspace_id
161
+ AND attempt.id = landmark.turn_attempt_id
162
+ JOIN session_turns turn
163
+ ON turn.workspace_id = attempt.workspace_id
164
+ AND turn.id = attempt.turn_id
165
+ AND turn.active_attempt_id = attempt.id
166
+ WHERE landmark.type = 'session.context.compaction.started'
167
+ AND landmark.payload ->> 'trigger' = 'auto'
168
+ AND attempt.state IN ('claimed', 'running')
169
+ ON CONFLICT (workspace_id, attempt_id) DO UPDATE
170
+ SET started_at = EXCLUDED.started_at;
171
+
172
+ ALTER TABLE session_turns FORCE ROW LEVEL SECURITY;
173
+ ALTER TABLE session_turn_attempts FORCE ROW LEVEL SECURITY;
174
+ ALTER TABLE session_events FORCE ROW LEVEL SECURITY;
175
+
176
+ CREATE OR REPLACE FUNCTION opengeni_private.context_compaction_pending_summary()
177
+ RETURNS TABLE (pending_count bigint, oldest_started_at timestamp with time zone)
178
+ LANGUAGE sql
179
+ STABLE
180
+ SECURITY DEFINER
181
+ SET search_path = pg_catalog
182
+ AS $context_compaction_pending_summary$
183
+ SELECT
184
+ count(*)::bigint AS pending_count,
185
+ min(pending.started_at) AS oldest_started_at
186
+ FROM opengeni_private.context_compaction_pending_observations pending
187
+ $context_compaction_pending_summary$;
188
+
189
+ REVOKE ALL ON FUNCTION opengeni_private.context_compaction_pending_summary() FROM PUBLIC;
190
+
191
+ DO $context_compaction_observability_grants$
192
+ BEGIN
193
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
194
+ GRANT EXECUTE ON FUNCTION opengeni_private.context_compaction_pending_summary()
195
+ TO opengeni_app;
196
+ END IF;
197
+ END
198
+ $context_compaction_observability_grants$;
@@ -0,0 +1,102 @@
1
+ -- deployment-mode: rolling
2
+ -- Migration 0393: enable Workspace Memory by default and make approval-required
3
+ -- workspace instruction/Skill learning the default when no policy revision has
4
+ -- been activated.
5
+ --
6
+ -- Explicit memory opt-outs and activated learning-policy revisions remain
7
+ -- authoritative. Existing immutable turn and learning snapshots are untouched.
8
+ -- Materializing the Memory default in workspace settings keeps old and new
9
+ -- rolling binaries aligned while the application resolver changes omission to
10
+ -- enabled.
11
+
12
+ SET LOCAL lock_timeout = '5s';
13
+ SET LOCAL statement_timeout = '5min';
14
+
15
+ ALTER TABLE "workspaces"
16
+ ALTER COLUMN "settings" SET DEFAULT '{"memoryEnabled": true}'::jsonb;
17
+
18
+ UPDATE "workspaces"
19
+ SET "settings" = jsonb_set("settings", '{memoryEnabled}', 'true'::jsonb, true)
20
+ WHERE NOT ("settings" ? 'memoryEnabled');
21
+
22
+ -- Historical no-revision snapshots were frozen with workspace_mode = 'off'.
23
+ -- Keep those rows valid while allowing newly accepted attempts to freeze the
24
+ -- approval-required default below.
25
+ ALTER TABLE "workspace_learning_policy_snapshots"
26
+ DROP CONSTRAINT "workspace_learning_policy_snapshots_identity_chk";
27
+
28
+ ALTER TABLE "workspace_learning_policy_snapshots"
29
+ ADD CONSTRAINT "workspace_learning_policy_snapshots_identity_chk" CHECK (
30
+ (
31
+ "revision_id" IS NULL
32
+ AND "revision" IS NULL
33
+ AND "policy_hash" IS NULL
34
+ AND "activation_version" = 0
35
+ AND "activated_at" IS NULL
36
+ AND "workspace_mode" IN ('off', 'suggest')
37
+ AND "source_overrides" = '[]'::jsonb
38
+ )
39
+ OR (
40
+ "revision_id" IS NOT NULL
41
+ AND "revision" > 0
42
+ AND "policy_hash" ~ '^[0-9a-f]{64}$'
43
+ AND "activation_version" > 0
44
+ AND "activated_at" IS NOT NULL
45
+ )
46
+ ) NOT VALID;
47
+
48
+ ALTER TABLE "workspace_learning_policy_snapshots"
49
+ VALIDATE CONSTRAINT "workspace_learning_policy_snapshots_identity_chk";
50
+
51
+ CREATE OR REPLACE FUNCTION workspace_learning_policy_canonical_at(
52
+ p_account_id uuid,
53
+ p_workspace_id uuid,
54
+ p_accepted_at timestamptz
55
+ ) RETURNS jsonb
56
+ LANGUAGE sql
57
+ STABLE
58
+ AS $$
59
+ SELECT CASE WHEN active."id" IS NULL THEN
60
+ jsonb_build_object(
61
+ 'revisionId', NULL,
62
+ 'revision', NULL,
63
+ 'policyHash', NULL,
64
+ 'activationVersion', 0,
65
+ 'activatedAt', NULL,
66
+ 'workspaceMode', 'suggest',
67
+ 'sourceOverrides', '[]'::jsonb
68
+ )
69
+ ELSE
70
+ jsonb_build_object(
71
+ 'revisionId', revision."id"::text,
72
+ 'revision', revision."revision",
73
+ 'policyHash', revision."policy_hash",
74
+ 'activationVersion', active."activation_version",
75
+ 'activatedAt', to_char(
76
+ active."created_at" AT TIME ZONE 'UTC',
77
+ 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"'
78
+ ),
79
+ 'workspaceMode', revision."workspace_mode",
80
+ 'sourceOverrides', revision."source_overrides"
81
+ )
82
+ END
83
+ FROM (SELECT 1) singleton
84
+ LEFT JOIN LATERAL (
85
+ SELECT event.*
86
+ FROM "workspace_learning_policy_activation_events" event
87
+ WHERE event."account_id" = p_account_id
88
+ AND event."workspace_id" = p_workspace_id
89
+ AND event."created_at" <= p_accepted_at
90
+ ORDER BY event."created_at" DESC, event."activation_version" DESC, event."id" DESC
91
+ LIMIT 1
92
+ ) active ON true
93
+ LEFT JOIN "workspace_learning_policy_revisions" revision
94
+ ON revision."account_id" = active."account_id"
95
+ AND revision."workspace_id" = active."workspace_id"
96
+ AND revision."id" = active."new_revision_id"
97
+ AND revision."revision" = active."new_revision"
98
+ AND revision."policy_hash" = active."new_policy_hash";
99
+ $$;
100
+
101
+ REVOKE ALL ON FUNCTION workspace_learning_policy_canonical_at(uuid, uuid, timestamptz)
102
+ FROM PUBLIC;
@@ -0,0 +1,20 @@
1
+ -- deployment-mode: maintenance
2
+ -- A session-selected Skill is installed and inspectable like any other Skill,
3
+ -- but the worker excludes it from ambient workspace resolution. An explicit
4
+ -- session create copies the immutable artifact onto that one session.
5
+
6
+ ALTER TABLE capability_facets
7
+ DROP CONSTRAINT capability_facets_activation_chk;
8
+
9
+ ALTER TABLE capability_facets
10
+ ADD CONSTRAINT capability_facets_activation_chk CHECK (
11
+ activation_mode IN (
12
+ 'platform_available',
13
+ 'runtime_baseline',
14
+ 'workspace_managed',
15
+ 'session_selected'
16
+ )
17
+ ) NOT VALID;
18
+
19
+ ALTER TABLE capability_facets
20
+ VALIDATE CONSTRAINT capability_facets_activation_chk;