@opengeni/db 3.7.4 → 4.0.0-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 (113) hide show
  1. package/dist/automations.d.ts +4 -0
  2. package/dist/canonical-human-identities.js +3 -3
  3. package/dist/capability-integrations.d.ts +2 -0
  4. package/dist/{chunk-6DPJA36C.js → chunk-2XEZK4LF.js} +7 -1
  5. package/dist/chunk-2XEZK4LF.js.map +1 -0
  6. package/dist/{chunk-4NAR7FBA.js → chunk-3RI33YQN.js} +67 -1
  7. package/dist/chunk-3RI33YQN.js.map +1 -0
  8. package/dist/{chunk-SZAWDXQC.js → chunk-CJROYHSC.js} +3 -3
  9. package/dist/{chunk-DZCYW2WT.js → chunk-MX6V5F65.js} +2 -2
  10. package/dist/{chunk-S3TDDCS2.js → chunk-N5VXJ4I2.js} +151 -78
  11. package/dist/chunk-N5VXJ4I2.js.map +1 -0
  12. package/dist/{chunk-U4WPPEBH.js → chunk-NLFHMPUB.js} +2 -2
  13. package/dist/{chunk-5NNCGFQY.js → chunk-P6NSEG2S.js} +2 -2
  14. package/dist/{chunk-O56DM5GZ.js → chunk-QM6YOWHZ.js} +624 -57
  15. package/dist/chunk-QM6YOWHZ.js.map +1 -0
  16. package/dist/{chunk-US4IS6IV.js → chunk-RZNOSEMC.js} +2 -2
  17. package/dist/{chunk-F6PBS6WE.js → chunk-UFAMOUWL.js} +2 -2
  18. package/dist/codemode-operations.d.ts +5 -0
  19. package/dist/codex-token-resolver.d.ts +6 -2
  20. package/dist/connection-token-resolver.d.ts +8 -0
  21. package/dist/editable-artifact-durable-export.js +2 -2
  22. package/dist/editable-artifacts.js +3 -3
  23. package/dist/generated-images.d.ts +15 -0
  24. package/dist/index.d.ts +393 -91
  25. package/dist/index.js +9992 -6069
  26. package/dist/index.js.map +1 -1
  27. package/dist/insights.d.ts +9 -0
  28. package/dist/managed-auth-session-sets.js +3 -3
  29. package/dist/managed-user-setup.d.ts +3 -0
  30. package/dist/mcp-oauth.d.ts +80 -0
  31. package/dist/migrate.d.ts +1 -0
  32. package/dist/migrate.js +3 -1
  33. package/dist/model-catalog.d.ts +153 -0
  34. package/dist/model-context-snapshots.d.ts +20 -0
  35. package/dist/new-session-drafts.d.ts +12 -0
  36. package/dist/organization-membership-lifecycle.d.ts +8 -1
  37. package/dist/organization-model-providers.d.ts +101 -0
  38. package/dist/pack-components.d.ts +1 -0
  39. package/dist/pr-review.d.ts +9 -0
  40. package/dist/provision-roles.js +1 -1
  41. package/dist/runtime-posture.d.ts +8 -8
  42. package/dist/schema.d.ts +7708 -5050
  43. package/dist/schema.js +25 -1
  44. package/dist/session-background-commands.d.ts +18 -6
  45. package/dist/session-background-commands.js +15 -9
  46. package/dist/session-control.d.ts +37 -2
  47. package/dist/session-goal-pacing.d.ts +3 -6
  48. package/dist/session-queue-commands.d.ts +17 -0
  49. package/dist/session-tenancy.js +3 -3
  50. package/dist/tool-gateway-approvals.d.ts +31 -0
  51. package/dist/video-generation.js +3 -3
  52. package/dist/workspace-artifacts.d.ts +17 -3
  53. package/dist/xai-subscription.d.ts +8 -0
  54. package/drizzle/0387_bound_host_export_session_payloads.sql +165 -0
  55. package/drizzle/0388_sandbox_provider_deadline_interactions.sql +491 -0
  56. package/drizzle/0389_model_catalog_and_gateway_custom_models.sql +241 -0
  57. package/drizzle/0390_organization_model_provider_connections.sql +197 -0
  58. package/drizzle/0391_sandbox_provider_deadline_interaction_followup.sql +295 -0
  59. package/drizzle/0392_context_compaction_pending_observability.sql +198 -0
  60. package/drizzle/0393_workspace_memory_and_learning_defaults.sql +102 -0
  61. package/drizzle/0394_session_selected_skill_activation.sql +20 -0
  62. package/drizzle/0395_scheduled_task_unclaimed_occurrence_invalidation.sql +154 -0
  63. package/drizzle/0396_model_call_equivalent_credit_cost.sql +28 -0
  64. package/drizzle/0397_sandbox_deadline_rotation_preemption.sql +113 -0
  65. package/drizzle/0398_organization_workspace_management_entry.sql +206 -0
  66. package/drizzle/0399_additional_managed_organization_creation.sql +691 -0
  67. package/drizzle/0400_session_attempt_model_context_snapshots.sql +65 -0
  68. package/drizzle/0401_organization_user_setup_token_transport.sql +178 -0
  69. package/drizzle/0402_session_input_wait_and_background_command_results.sql +1023 -0
  70. package/drizzle/0403_codex_unconditional_credential_leasing.sql +181 -0
  71. package/drizzle/0404_mcp_oauth_authorization_server.sql +471 -0
  72. package/drizzle/0405_tool_gateway_approval_capabilities.sql +184 -0
  73. package/drizzle/0406_workspace_html_site_sources.sql +71 -0
  74. package/package.json +6 -6
  75. package/src/automations.ts +9 -1
  76. package/src/capability-integrations.ts +8 -0
  77. package/src/codemode-operations.ts +31 -21
  78. package/src/codex-token-resolver.ts +16 -6
  79. package/src/connection-token-resolver.ts +52 -3
  80. package/src/generated-images.ts +70 -6
  81. package/src/index.ts +3894 -994
  82. package/src/insights-model-bundle.ts +54 -5
  83. package/src/insights.ts +53 -7
  84. package/src/managed-user-setup.ts +14 -3
  85. package/src/mcp-oauth.ts +488 -0
  86. package/src/migrate.ts +9 -1
  87. package/src/model-catalog.ts +682 -0
  88. package/src/model-context-snapshots.ts +151 -0
  89. package/src/new-session-drafts.ts +66 -21
  90. package/src/organization-membership-lifecycle.ts +47 -5
  91. package/src/organization-model-providers.ts +614 -0
  92. package/src/pack-components.ts +15 -2
  93. package/src/pr-review.ts +19 -0
  94. package/src/provision-roles.ts +4 -0
  95. package/src/runtime-posture.ts +64 -0
  96. package/src/schema.ts +666 -61
  97. package/src/session-background-commands.ts +180 -78
  98. package/src/session-control.ts +181 -12
  99. package/src/session-goal-pacing.ts +4 -8
  100. package/src/session-queue-commands.ts +157 -82
  101. package/src/tool-gateway-approvals.ts +168 -0
  102. package/src/workspace-artifacts.ts +633 -178
  103. package/src/xai-subscription.ts +66 -14
  104. package/dist/chunk-4NAR7FBA.js.map +0 -1
  105. package/dist/chunk-6DPJA36C.js.map +0 -1
  106. package/dist/chunk-O56DM5GZ.js.map +0 -1
  107. package/dist/chunk-S3TDDCS2.js.map +0 -1
  108. /package/dist/{chunk-SZAWDXQC.js.map → chunk-CJROYHSC.js.map} +0 -0
  109. /package/dist/{chunk-DZCYW2WT.js.map → chunk-MX6V5F65.js.map} +0 -0
  110. /package/dist/{chunk-U4WPPEBH.js.map → chunk-NLFHMPUB.js.map} +0 -0
  111. /package/dist/{chunk-5NNCGFQY.js.map → chunk-P6NSEG2S.js.map} +0 -0
  112. /package/dist/{chunk-US4IS6IV.js.map → chunk-RZNOSEMC.js.map} +0 -0
  113. /package/dist/{chunk-F6PBS6WE.js.map → chunk-UFAMOUWL.js.map} +0 -0
@@ -0,0 +1,1023 @@
1
+ -- deployment-mode: maintenance
2
+
3
+ -- Move the durable out-of-turn wait from an active goal to its owning session.
4
+ -- This is an intentional clean break: the first-party tool is now
5
+ -- `wait_for_input`, it does not require a goal, and there is no compatibility
6
+ -- alias for `goal_wait`. Existing declarations keep their exact turn,
7
+ -- deadline, reason, and set time across the cutover. Existing explicit tool
8
+ -- selections replace `goal_wait` with `wait_for_input` in place across
9
+ -- sessions, workspace defaults, private drafts, automation definitions and
10
+ -- accepted runs, scheduled accepted-execution snapshots, and registered or
11
+ -- installed Pack manifests. Digest-bound snapshots are rewritten with their
12
+ -- exact native digest authority in the same transaction.
13
+ -- Stop every API, control worker, and turn worker before applying this
14
+ -- migration, and never restart a pre-0402 image after commit.
15
+
16
+ SET LOCAL lock_timeout = '5s';
17
+ SET LOCAL statement_timeout = '30min';
18
+
19
+ DO $session_input_wait_runtime_drain_before$
20
+ DECLARE
21
+ configured_roles_text text := nullif(
22
+ current_setting('opengeni.migration_application_roles', true), ''
23
+ );
24
+ configured_roles jsonb;
25
+ BEGIN
26
+ IF configured_roles_text IS NULL THEN
27
+ RAISE EXCEPTION
28
+ '0402 session input wait activation requires an explicit application database role list'
29
+ USING ERRCODE = '55000';
30
+ END IF;
31
+ BEGIN
32
+ configured_roles := configured_roles_text::jsonb;
33
+ EXCEPTION WHEN OTHERS THEN
34
+ RAISE EXCEPTION
35
+ '0402 session input wait activation received a malformed application database role list'
36
+ USING ERRCODE = '55000';
37
+ END;
38
+ IF jsonb_typeof(configured_roles) <> 'array'
39
+ OR jsonb_array_length(configured_roles) NOT BETWEEN 1 AND 16
40
+ OR EXISTS (
41
+ SELECT 1 FROM jsonb_array_elements(configured_roles) AS roles(value)
42
+ WHERE jsonb_typeof(value) <> 'string'
43
+ OR btrim(value #>> '{}') = ''
44
+ OR octet_length(value #>> '{}') > 63
45
+ )
46
+ OR (
47
+ SELECT count(*) FROM jsonb_array_elements_text(configured_roles)
48
+ ) <> (
49
+ SELECT count(DISTINCT value)
50
+ FROM jsonb_array_elements_text(configured_roles) AS roles(value)
51
+ )
52
+ THEN
53
+ RAISE EXCEPTION
54
+ '0402 session input wait activation received an invalid application database role list'
55
+ USING ERRCODE = '55000';
56
+ END IF;
57
+ IF EXISTS (
58
+ SELECT 1 FROM pg_stat_activity activity
59
+ JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
60
+ ON roles.role_name = activity.usename
61
+ WHERE activity.datname = current_database()
62
+ AND activity.pid <> pg_backend_pid()
63
+ )
64
+ THEN
65
+ RAISE EXCEPTION
66
+ '0402 session input wait activation requires all configured OpenGeni application database sessions to be stopped'
67
+ USING ERRCODE = '55000';
68
+ END IF;
69
+ END
70
+ $session_input_wait_runtime_drain_before$;
71
+
72
+ -- The runtime drain is a process fence. Hold every affected persistence
73
+ -- authority too, so no migration/admin writer can create a stale snapshot
74
+ -- between inventory, digest proof, and rewrite.
75
+ LOCK TABLE
76
+ "workspaces",
77
+ "sessions",
78
+ "session_goals",
79
+ "new_session_drafts",
80
+ "automation_trigger_revisions",
81
+ "automation_runs",
82
+ "scheduled_tasks",
83
+ "scheduled_task_revision_authorities",
84
+ "scheduled_task_personal_resource_authorities",
85
+ "scheduled_task_connection_authority_snapshots",
86
+ "scheduled_task_runs",
87
+ "workspace_packs",
88
+ "pack_installations",
89
+ "capability_operations"
90
+ IN ACCESS EXCLUSIVE MODE;
91
+
92
+ -- Preserve exact first-seen order. The rename may make an existing
93
+ -- wait_for_input entry collide with one or more historical goal_wait entries;
94
+ -- collapse only that replacement, leaving every unrelated entry byte-order
95
+ -- equivalent (including any unrelated duplicates).
96
+ CREATE FUNCTION opengeni_private.session_wait_translate_tool_array(value jsonb)
97
+ RETURNS jsonb
98
+ LANGUAGE plpgsql
99
+ IMMUTABLE
100
+ STRICT
101
+ PARALLEL SAFE
102
+ AS $translate$
103
+ DECLARE
104
+ translated jsonb;
105
+ BEGIN
106
+ IF jsonb_typeof(value) <> 'array' OR EXISTS (
107
+ SELECT 1
108
+ FROM jsonb_array_elements(value) AS entry(item)
109
+ WHERE jsonb_typeof(entry.item) <> 'string'
110
+ ) THEN
111
+ RAISE EXCEPTION 'persisted first-party tool selection must be a string array'
112
+ USING ERRCODE = '23514';
113
+ END IF;
114
+
115
+ WITH mapped AS (
116
+ SELECT
117
+ CASE entry.value #>> '{}'
118
+ WHEN 'goal_wait' THEN 'wait_for_input'
119
+ ELSE entry.value #>> '{}'
120
+ END AS tool,
121
+ entry.ordinality
122
+ FROM jsonb_array_elements(value) WITH ORDINALITY AS entry(value, ordinality)
123
+ ), ranked AS (
124
+ SELECT
125
+ mapped.tool,
126
+ mapped.ordinality,
127
+ row_number() OVER (
128
+ PARTITION BY mapped.tool
129
+ ORDER BY mapped.ordinality
130
+ ) AS tool_ordinality
131
+ FROM mapped
132
+ )
133
+ SELECT coalesce(
134
+ jsonb_agg(to_jsonb(ranked.tool) ORDER BY ranked.ordinality),
135
+ '[]'::jsonb
136
+ )
137
+ INTO translated
138
+ FROM ranked
139
+ WHERE ranked.tool <> 'wait_for_input' OR ranked.tool_ordinality = 1;
140
+
141
+ RETURN translated;
142
+ END
143
+ $translate$;
144
+
145
+ CREATE FUNCTION opengeni_private.session_wait_rewrite_automation_template(value jsonb)
146
+ RETURNS jsonb
147
+ LANGUAGE plpgsql
148
+ IMMUTABLE
149
+ STRICT
150
+ PARALLEL SAFE
151
+ AS $rewrite_automation_template$
152
+ BEGIN
153
+ IF jsonb_typeof(value) <> 'object'
154
+ OR jsonb_typeof(value -> 'firstPartyMcpTools') <> 'array'
155
+ THEN
156
+ RAISE EXCEPTION 'persisted automation session template has invalid tool selection'
157
+ USING ERRCODE = '23514';
158
+ END IF;
159
+ IF value -> 'firstPartyMcpTools' @> '["goal_wait"]'::jsonb THEN
160
+ RETURN jsonb_set(
161
+ value,
162
+ '{firstPartyMcpTools}',
163
+ opengeni_private.session_wait_translate_tool_array(
164
+ value -> 'firstPartyMcpTools'
165
+ ),
166
+ false
167
+ );
168
+ END IF;
169
+ RETURN value;
170
+ END
171
+ $rewrite_automation_template$;
172
+
173
+ CREATE FUNCTION opengeni_private.session_wait_rewrite_pack(value jsonb)
174
+ RETURNS jsonb
175
+ LANGUAGE plpgsql
176
+ IMMUTABLE
177
+ STRICT
178
+ PARALLEL SAFE
179
+ AS $rewrite_pack$
180
+ DECLARE
181
+ templates jsonb;
182
+ BEGIN
183
+ IF jsonb_typeof(value) <> 'object'
184
+ OR NOT (value ? 'automationTemplates')
185
+ OR jsonb_typeof(value -> 'automationTemplates') <> 'array'
186
+ THEN
187
+ RETURN value;
188
+ END IF;
189
+
190
+ SELECT coalesce(
191
+ jsonb_agg(
192
+ CASE
193
+ WHEN template.value #> '{sessionTemplate,firstPartyMcpTools}'
194
+ @> '["goal_wait"]'::jsonb
195
+ THEN jsonb_set(
196
+ template.value,
197
+ '{sessionTemplate}',
198
+ opengeni_private.session_wait_rewrite_automation_template(
199
+ template.value -> 'sessionTemplate'
200
+ ),
201
+ false
202
+ )
203
+ ELSE template.value
204
+ END
205
+ ORDER BY template.ordinality
206
+ ),
207
+ '[]'::jsonb
208
+ )
209
+ INTO templates
210
+ FROM jsonb_array_elements(value -> 'automationTemplates')
211
+ WITH ORDINALITY AS template(value, ordinality);
212
+
213
+ RETURN jsonb_set(value, '{automationTemplates}', templates, false);
214
+ END
215
+ $rewrite_pack$;
216
+
217
+ CREATE FUNCTION opengeni_private.session_wait_rewrite_scheduled_agent_config(value jsonb)
218
+ RETURNS jsonb
219
+ LANGUAGE plpgsql
220
+ IMMUTABLE
221
+ STRICT
222
+ PARALLEL SAFE
223
+ AS $rewrite_scheduled_agent_config$
224
+ DECLARE
225
+ rewritten jsonb := value;
226
+ BEGIN
227
+ IF jsonb_typeof(value) <> 'object' THEN
228
+ RAISE EXCEPTION 'persisted scheduled agent config must be an object'
229
+ USING ERRCODE = '23514';
230
+ END IF;
231
+
232
+ IF rewritten #> '{incidentTelemetryPreflight,requiredFirstPartyMcpTools}'
233
+ @> '["goal_wait"]'::jsonb
234
+ THEN
235
+ rewritten := jsonb_set(
236
+ rewritten,
237
+ '{incidentTelemetryPreflight,requiredFirstPartyMcpTools}',
238
+ opengeni_private.session_wait_translate_tool_array(
239
+ rewritten #> '{incidentTelemetryPreflight,requiredFirstPartyMcpTools}'
240
+ ),
241
+ false
242
+ );
243
+ END IF;
244
+ IF rewritten #>> '{incidentTelemetryPreflight,dataSource,route,kind}' = 'first_party'
245
+ AND rewritten #>> '{incidentTelemetryPreflight,dataSource,route,tool}' = 'goal_wait'
246
+ THEN
247
+ rewritten := jsonb_set(
248
+ rewritten,
249
+ '{incidentTelemetryPreflight,dataSource,route,tool}',
250
+ to_jsonb('wait_for_input'::text),
251
+ false
252
+ );
253
+ END IF;
254
+ RETURN rewritten;
255
+ END
256
+ $rewrite_scheduled_agent_config$;
257
+
258
+ CREATE FUNCTION opengeni_private.session_wait_rewrite_scheduled_execution(value jsonb)
259
+ RETURNS jsonb
260
+ LANGUAGE plpgsql
261
+ IMMUTABLE
262
+ STRICT
263
+ PARALLEL SAFE
264
+ AS $rewrite_scheduled_execution$
265
+ DECLARE
266
+ rewritten jsonb := value;
267
+ BEGIN
268
+ IF jsonb_typeof(value) <> 'object' THEN
269
+ RAISE EXCEPTION 'persisted scheduled accepted execution must be an object'
270
+ USING ERRCODE = '23514';
271
+ END IF;
272
+
273
+ IF rewritten -> 'resolvedFirstPartyMcpTools' @> '["goal_wait"]'::jsonb THEN
274
+ rewritten := jsonb_set(
275
+ rewritten,
276
+ '{resolvedFirstPartyMcpTools}',
277
+ opengeni_private.session_wait_translate_tool_array(
278
+ rewritten -> 'resolvedFirstPartyMcpTools'
279
+ ),
280
+ false
281
+ );
282
+ END IF;
283
+ IF rewritten #> '{targetSessionExecution,firstPartyMcpTools}'
284
+ @> '["goal_wait"]'::jsonb
285
+ THEN
286
+ rewritten := jsonb_set(
287
+ rewritten,
288
+ '{targetSessionExecution,firstPartyMcpTools}',
289
+ opengeni_private.session_wait_translate_tool_array(
290
+ rewritten #> '{targetSessionExecution,firstPartyMcpTools}'
291
+ ),
292
+ false
293
+ );
294
+ END IF;
295
+ IF rewritten #> '{task,agentConfig}' IS DISTINCT FROM
296
+ opengeni_private.session_wait_rewrite_scheduled_agent_config(
297
+ rewritten #> '{task,agentConfig}'
298
+ )
299
+ THEN
300
+ rewritten := jsonb_set(
301
+ rewritten,
302
+ '{task,agentConfig}',
303
+ opengeni_private.session_wait_rewrite_scheduled_agent_config(
304
+ rewritten #> '{task,agentConfig}'
305
+ ),
306
+ false
307
+ );
308
+ END IF;
309
+ RETURN rewritten;
310
+ END
311
+ $rewrite_scheduled_execution$;
312
+
313
+ -- Pack manifests are hashed with contracts stableJson(), whose object-key
314
+ -- order uses the default en-US Intl comparator. Reproduce those exact bytes;
315
+ -- the old-digest precondition below aborts before mutation if this database's
316
+ -- ICU/scalar rendering cannot reproduce any affected stored digest.
317
+ CREATE COLLATION opengeni_private.session_wait_js_en_us (
318
+ provider = icu,
319
+ locale = 'en-US',
320
+ deterministic = false
321
+ );
322
+
323
+ CREATE FUNCTION opengeni_private.session_wait_canonical_json(value jsonb)
324
+ RETURNS text
325
+ LANGUAGE plpgsql
326
+ IMMUTABLE
327
+ STRICT
328
+ PARALLEL SAFE
329
+ AS $canonical_json$
330
+ DECLARE
331
+ rendered text;
332
+ BEGIN
333
+ CASE jsonb_typeof(value)
334
+ WHEN 'object' THEN
335
+ SELECT coalesce(
336
+ '{' || string_agg(
337
+ to_jsonb(entry.key)::text || ':' ||
338
+ opengeni_private.session_wait_canonical_json(entry.value),
339
+ ',' ORDER BY
340
+ entry.key COLLATE opengeni_private.session_wait_js_en_us,
341
+ entry.ordinality
342
+ ) || '}',
343
+ '{}'
344
+ )
345
+ INTO rendered
346
+ FROM jsonb_each(value) WITH ORDINALITY AS entry(key, value, ordinality);
347
+ RETURN rendered;
348
+ WHEN 'array' THEN
349
+ SELECT coalesce(
350
+ '[' || string_agg(
351
+ opengeni_private.session_wait_canonical_json(entry.value),
352
+ ',' ORDER BY entry.ordinality
353
+ ) || ']',
354
+ '[]'
355
+ )
356
+ INTO rendered
357
+ FROM jsonb_array_elements(value) WITH ORDINALITY AS entry(value, ordinality);
358
+ RETURN rendered;
359
+ ELSE
360
+ RETURN value::text;
361
+ END CASE;
362
+ END
363
+ $canonical_json$;
364
+
365
+ ALTER TABLE "sessions"
366
+ ADD COLUMN "input_wait_turn_id" uuid,
367
+ ADD COLUMN "input_wait_until" timestamptz,
368
+ ADD COLUMN "input_wait_reason" text,
369
+ ADD COLUMN "input_wait_set_at" timestamptz;
370
+
371
+ -- The production migration identity is a NOSUPERUSER/NOBYPASSRLS table owner.
372
+ -- Relax FORCE only for this drained owner backfill, then restore it before any
373
+ -- constraint or column cutover. The enclosing migration transaction makes a
374
+ -- failure rollback both posture changes.
375
+ ALTER TABLE "sessions" NO FORCE ROW LEVEL SECURITY;
376
+ ALTER TABLE "session_goals" NO FORCE ROW LEVEL SECURITY;
377
+ ALTER TABLE "new_session_drafts" NO FORCE ROW LEVEL SECURITY;
378
+ ALTER TABLE "automation_trigger_revisions" NO FORCE ROW LEVEL SECURITY;
379
+ ALTER TABLE "automation_runs" NO FORCE ROW LEVEL SECURITY;
380
+ ALTER TABLE "scheduled_tasks" NO FORCE ROW LEVEL SECURITY;
381
+ ALTER TABLE "scheduled_task_revision_authorities" NO FORCE ROW LEVEL SECURITY;
382
+ ALTER TABLE "scheduled_task_personal_resource_authorities" NO FORCE ROW LEVEL SECURITY;
383
+ ALTER TABLE "scheduled_task_connection_authority_snapshots" NO FORCE ROW LEVEL SECURITY;
384
+ ALTER TABLE "scheduled_task_runs" NO FORCE ROW LEVEL SECURITY;
385
+ ALTER TABLE "workspace_packs" NO FORCE ROW LEVEL SECURITY;
386
+ ALTER TABLE "pack_installations" NO FORCE ROW LEVEL SECURITY;
387
+ ALTER TABLE "capability_operations" NO FORCE ROW LEVEL SECURITY;
388
+
389
+ CREATE TEMP TABLE session_wait_scheduled_task_cutover ON COMMIT DROP AS
390
+ SELECT
391
+ task.id,
392
+ task.account_id,
393
+ task.workspace_id,
394
+ task.authority_revision,
395
+ task.execution_digest AS previous_execution_digest,
396
+ task.agent_config AS previous_agent_config,
397
+ opengeni_private.session_wait_rewrite_scheduled_agent_config(
398
+ task.agent_config
399
+ ) AS agent_config
400
+ FROM scheduled_tasks task
401
+ WHERE task.agent_config IS DISTINCT FROM
402
+ opengeni_private.session_wait_rewrite_scheduled_agent_config(
403
+ task.agent_config
404
+ );
405
+
406
+ CREATE TEMP TABLE session_wait_scheduled_snapshot_cutover ON COMMIT DROP AS
407
+ SELECT
408
+ run.id,
409
+ run.workspace_id,
410
+ run.accepted_execution_snapshot AS previous_snapshot,
411
+ run.accepted_execution_digest AS previous_digest,
412
+ opengeni_private.session_wait_rewrite_scheduled_execution(
413
+ run.accepted_execution_snapshot
414
+ ) AS accepted_execution_snapshot
415
+ FROM scheduled_task_runs run
416
+ WHERE run.accepted_execution_snapshot IS NOT NULL
417
+ AND run.accepted_execution_snapshot IS DISTINCT FROM
418
+ opengeni_private.session_wait_rewrite_scheduled_execution(
419
+ run.accepted_execution_snapshot
420
+ );
421
+
422
+ CREATE TEMP TABLE session_wait_pack_snapshot_cutover ON COMMIT DROP AS
423
+ SELECT
424
+ installation.id,
425
+ installation.workspace_id,
426
+ installation.pack_id,
427
+ installation.manifest_snapshot AS previous_manifest_snapshot,
428
+ installation.manifest_digest AS previous_manifest_digest,
429
+ opengeni_private.session_wait_rewrite_pack(
430
+ installation.manifest_snapshot
431
+ ) AS manifest_snapshot,
432
+ encode(
433
+ digest(
434
+ convert_to(
435
+ opengeni_private.session_wait_canonical_json(
436
+ opengeni_private.session_wait_rewrite_pack(
437
+ installation.manifest_snapshot
438
+ )
439
+ ),
440
+ 'UTF8'
441
+ ),
442
+ 'sha256'
443
+ ),
444
+ 'hex'
445
+ ) AS manifest_digest
446
+ FROM pack_installations installation
447
+ WHERE installation.manifest_snapshot IS NOT NULL
448
+ AND installation.manifest_snapshot IS DISTINCT FROM
449
+ opengeni_private.session_wait_rewrite_pack(
450
+ installation.manifest_snapshot
451
+ );
452
+
453
+ DO $session_wait_snapshot_preconditions$
454
+ BEGIN
455
+ IF EXISTS (
456
+ SELECT 1
457
+ FROM session_wait_scheduled_task_cutover migrated
458
+ JOIN scheduled_tasks task ON task.id = migrated.id
459
+ WHERE migrated.previous_execution_digest IS DISTINCT FROM
460
+ scheduled_task_execution_digest(task)
461
+ ) THEN
462
+ RAISE EXCEPTION
463
+ '0402 cannot reproduce a stored scheduled-task execution digest'
464
+ USING ERRCODE = '23514';
465
+ END IF;
466
+
467
+ IF EXISTS (
468
+ SELECT 1
469
+ FROM session_wait_scheduled_task_cutover migrated
470
+ JOIN scheduled_task_revision_authorities authority
471
+ ON authority.task_id = migrated.id
472
+ AND authority.task_authority_revision = migrated.authority_revision
473
+ WHERE authority.execution_digest IS DISTINCT FROM
474
+ migrated.previous_execution_digest
475
+ ) OR EXISTS (
476
+ SELECT 1
477
+ FROM session_wait_scheduled_task_cutover migrated
478
+ JOIN scheduled_task_personal_resource_authorities authority
479
+ ON authority.task_id = migrated.id
480
+ AND authority.task_authority_revision = migrated.authority_revision
481
+ WHERE authority.execution_digest IS DISTINCT FROM
482
+ migrated.previous_execution_digest
483
+ ) OR EXISTS (
484
+ SELECT 1
485
+ FROM session_wait_scheduled_task_cutover migrated
486
+ JOIN scheduled_task_connection_authority_snapshots snapshot
487
+ ON snapshot.task_id = migrated.id
488
+ AND snapshot.task_authority_revision = migrated.authority_revision
489
+ WHERE snapshot.execution_digest IS DISTINCT FROM
490
+ migrated.previous_execution_digest
491
+ OR snapshot.canonical_snapshot ->> 'executionDigest' IS DISTINCT FROM
492
+ migrated.previous_execution_digest
493
+ OR snapshot.snapshot_digest IS DISTINCT FROM digest(
494
+ convert_to(snapshot.canonical_snapshot::text, 'UTF8'),
495
+ 'sha256'
496
+ )
497
+ ) THEN
498
+ RAISE EXCEPTION
499
+ '0402 scheduled-task authority digest evidence is inconsistent'
500
+ USING ERRCODE = '23514';
501
+ END IF;
502
+
503
+ IF EXISTS (
504
+ SELECT 1
505
+ FROM session_wait_scheduled_snapshot_cutover migrated
506
+ WHERE migrated.previous_digest IS DISTINCT FROM encode(
507
+ digest(convert_to(migrated.previous_snapshot::text, 'UTF8'), 'sha256'),
508
+ 'hex'
509
+ )
510
+ ) THEN
511
+ RAISE EXCEPTION
512
+ '0402 cannot reproduce a stored scheduled accepted-execution digest'
513
+ USING ERRCODE = '23514';
514
+ END IF;
515
+
516
+ IF EXISTS (
517
+ SELECT 1
518
+ FROM session_wait_pack_snapshot_cutover migrated
519
+ WHERE migrated.previous_manifest_digest IS DISTINCT FROM encode(
520
+ digest(
521
+ convert_to(
522
+ opengeni_private.session_wait_canonical_json(
523
+ migrated.previous_manifest_snapshot
524
+ ),
525
+ 'UTF8'
526
+ ),
527
+ 'sha256'
528
+ ),
529
+ 'hex'
530
+ )
531
+ ) THEN
532
+ RAISE EXCEPTION
533
+ '0402 cannot reproduce a stored runtime Pack manifest digest'
534
+ USING ERRCODE = '23514';
535
+ END IF;
536
+
537
+ IF EXISTS (
538
+ SELECT 1
539
+ FROM capability_operations operation
540
+ JOIN (
541
+ SELECT pack.workspace_id, pack.pack_id
542
+ FROM workspace_packs pack
543
+ WHERE pack.manifest IS DISTINCT FROM
544
+ opengeni_private.session_wait_rewrite_pack(pack.manifest)
545
+ UNION
546
+ SELECT migrated.workspace_id, migrated.pack_id
547
+ FROM session_wait_pack_snapshot_cutover migrated
548
+ ) affected
549
+ ON affected.workspace_id = operation.workspace_id
550
+ AND affected.pack_id = operation.target_id
551
+ WHERE operation.target_kind = 'pack'
552
+ AND operation.status IN ('pending', 'running')
553
+ ) THEN
554
+ RAISE EXCEPTION
555
+ '0402 requires affected Pack operations to be settled before cutover'
556
+ USING ERRCODE = '55000';
557
+ END IF;
558
+ END
559
+ $session_wait_snapshot_preconditions$;
560
+
561
+ DO $session_input_wait_workspace_fences$
562
+ DECLARE
563
+ workspace_id_value uuid;
564
+ BEGIN
565
+ FOR workspace_id_value IN
566
+ SELECT affected."workspace_id"
567
+ FROM (
568
+ SELECT session."workspace_id"
569
+ FROM "sessions" AS session
570
+ JOIN "session_goals" AS goal
571
+ ON goal."workspace_id" = session."workspace_id"
572
+ AND goal."session_id" = session."id"
573
+ WHERE goal."continuation_hold_turn_id" IS NOT NULL
574
+ UNION
575
+ SELECT session."workspace_id"
576
+ FROM "sessions" AS session
577
+ WHERE session."first_party_mcp_tools" @> '["goal_wait"]'::jsonb
578
+ UNION
579
+ SELECT workspace.id
580
+ FROM workspaces workspace
581
+ WHERE workspace.settings #> '{sessionToolDefaults,firstPartyMcpTools}'
582
+ @> '["goal_wait"]'::jsonb
583
+ UNION
584
+ SELECT draft.workspace_id
585
+ FROM new_session_drafts draft
586
+ WHERE draft.session_options -> 'firstPartyMcpTools'
587
+ @> '["goal_wait"]'::jsonb
588
+ UNION
589
+ SELECT revision.workspace_id
590
+ FROM automation_trigger_revisions revision
591
+ WHERE revision.session_template -> 'firstPartyMcpTools'
592
+ @> '["goal_wait"]'::jsonb
593
+ UNION
594
+ SELECT run.workspace_id
595
+ FROM automation_runs run
596
+ WHERE run.accepted_execution #> '{sessionTemplate,firstPartyMcpTools}'
597
+ @> '["goal_wait"]'::jsonb
598
+ UNION
599
+ SELECT migrated.workspace_id
600
+ FROM session_wait_scheduled_task_cutover migrated
601
+ UNION
602
+ SELECT migrated.workspace_id
603
+ FROM session_wait_scheduled_snapshot_cutover migrated
604
+ UNION
605
+ SELECT pack.workspace_id
606
+ FROM workspace_packs pack
607
+ WHERE pack.manifest IS DISTINCT FROM
608
+ opengeni_private.session_wait_rewrite_pack(pack.manifest)
609
+ UNION
610
+ SELECT migrated.workspace_id
611
+ FROM session_wait_pack_snapshot_cutover migrated
612
+ ) AS affected
613
+ ORDER BY affected."workspace_id"
614
+ LOOP
615
+ PERFORM acquire_session_tenancy_fence(workspace_id_value);
616
+ END LOOP;
617
+ END
618
+ $session_input_wait_workspace_fences$;
619
+
620
+ UPDATE "sessions" AS session SET
621
+ "input_wait_turn_id" = goal."continuation_hold_turn_id",
622
+ "input_wait_until" = goal."continuation_hold_until",
623
+ "input_wait_reason" = coalesce(nullif(btrim(goal."continuation_hold_reason"), ''), 'Waiting for external input'),
624
+ "input_wait_set_at" = goal."continuation_hold_set_at"
625
+ FROM "session_goals" AS goal
626
+ WHERE goal."workspace_id" = session."workspace_id"
627
+ AND goal."session_id" = session."id"
628
+ AND goal."continuation_hold_turn_id" IS NOT NULL
629
+ AND goal."continuation_hold_until" IS NOT NULL
630
+ AND goal."continuation_hold_set_at" IS NOT NULL;
631
+
632
+ -- Tool selection is an immutable per-session snapshot. Preserve every
633
+ -- selected tool and its first-seen order while translating the clean-break
634
+ -- rename; collapse the replacement when a snapshot already contains both.
635
+ UPDATE "sessions" AS session
636
+ SET "first_party_mcp_tools" =
637
+ opengeni_private.session_wait_translate_tool_array(
638
+ session."first_party_mcp_tools"
639
+ )
640
+ WHERE session."first_party_mcp_tools" @> '["goal_wait"]'::jsonb;
641
+
642
+ UPDATE workspaces workspace
643
+ SET settings = jsonb_set(
644
+ workspace.settings,
645
+ '{sessionToolDefaults,firstPartyMcpTools}',
646
+ opengeni_private.session_wait_translate_tool_array(
647
+ workspace.settings #> '{sessionToolDefaults,firstPartyMcpTools}'
648
+ ),
649
+ false
650
+ )
651
+ WHERE workspace.settings #> '{sessionToolDefaults,firstPartyMcpTools}'
652
+ @> '["goal_wait"]'::jsonb;
653
+
654
+ UPDATE new_session_drafts draft
655
+ SET session_options = jsonb_set(
656
+ draft.session_options,
657
+ '{firstPartyMcpTools}',
658
+ opengeni_private.session_wait_translate_tool_array(
659
+ draft.session_options -> 'firstPartyMcpTools'
660
+ ),
661
+ false
662
+ )
663
+ WHERE draft.session_options -> 'firstPartyMcpTools'
664
+ @> '["goal_wait"]'::jsonb;
665
+
666
+ UPDATE automation_trigger_revisions revision
667
+ SET session_template =
668
+ opengeni_private.session_wait_rewrite_automation_template(
669
+ revision.session_template
670
+ )
671
+ WHERE revision.session_template -> 'firstPartyMcpTools'
672
+ @> '["goal_wait"]'::jsonb;
673
+
674
+ UPDATE automation_runs run
675
+ SET accepted_execution = jsonb_set(
676
+ run.accepted_execution,
677
+ '{sessionTemplate}',
678
+ opengeni_private.session_wait_rewrite_automation_template(
679
+ run.accepted_execution -> 'sessionTemplate'
680
+ ),
681
+ false
682
+ )
683
+ WHERE run.accepted_execution #> '{sessionTemplate,firstPartyMcpTools}'
684
+ @> '["goal_wait"]'::jsonb;
685
+
686
+ -- The protocol rename is authority-equivalent for an active scheduled-task
687
+ -- head. Preserve its exact revision while the normal digest trigger derives
688
+ -- the new execution digest, then move only the current-revision authority
689
+ -- headers and canonical Connection evidence to that digest. Ordinary runtime
690
+ -- updates still advance revisions through the two triggers disabled here.
691
+ ALTER TABLE scheduled_tasks
692
+ DISABLE TRIGGER scheduled_task_connection_authority_execution_revision;
693
+ ALTER TABLE scheduled_tasks
694
+ DISABLE TRIGGER scheduled_task_personal_resource_execution_revision;
695
+
696
+ UPDATE scheduled_tasks task
697
+ SET agent_config = migrated.agent_config
698
+ FROM session_wait_scheduled_task_cutover migrated
699
+ WHERE task.id = migrated.id;
700
+
701
+ UPDATE scheduled_task_revision_authorities authority
702
+ SET execution_digest = task.execution_digest
703
+ FROM session_wait_scheduled_task_cutover migrated
704
+ JOIN scheduled_tasks task ON task.id = migrated.id
705
+ WHERE authority.task_id = migrated.id
706
+ AND authority.task_authority_revision = migrated.authority_revision;
707
+
708
+ UPDATE scheduled_task_personal_resource_authorities authority
709
+ SET execution_digest = task.execution_digest
710
+ FROM session_wait_scheduled_task_cutover migrated
711
+ JOIN scheduled_tasks task ON task.id = migrated.id
712
+ WHERE authority.task_id = migrated.id
713
+ AND authority.task_authority_revision = migrated.authority_revision;
714
+
715
+ WITH rewritten AS (
716
+ SELECT
717
+ snapshot.task_id,
718
+ snapshot.task_authority_revision,
719
+ snapshot.server_id,
720
+ task.execution_digest,
721
+ jsonb_set(
722
+ snapshot.canonical_snapshot,
723
+ '{executionDigest}',
724
+ to_jsonb(task.execution_digest),
725
+ false
726
+ ) AS canonical_snapshot
727
+ FROM scheduled_task_connection_authority_snapshots snapshot
728
+ JOIN session_wait_scheduled_task_cutover migrated
729
+ ON migrated.id = snapshot.task_id
730
+ AND migrated.authority_revision = snapshot.task_authority_revision
731
+ JOIN scheduled_tasks task ON task.id = migrated.id
732
+ )
733
+ UPDATE scheduled_task_connection_authority_snapshots snapshot
734
+ SET execution_digest = rewritten.execution_digest,
735
+ canonical_snapshot = rewritten.canonical_snapshot,
736
+ snapshot_digest = digest(
737
+ convert_to(rewritten.canonical_snapshot::text, 'UTF8'),
738
+ 'sha256'
739
+ )
740
+ FROM rewritten
741
+ WHERE snapshot.task_id = rewritten.task_id
742
+ AND snapshot.task_authority_revision = rewritten.task_authority_revision
743
+ AND snapshot.server_id = rewritten.server_id;
744
+
745
+ ALTER TABLE scheduled_tasks
746
+ ENABLE TRIGGER scheduled_task_personal_resource_execution_revision;
747
+ ALTER TABLE scheduled_tasks
748
+ ENABLE TRIGGER scheduled_task_connection_authority_execution_revision;
749
+
750
+ -- Accepted scheduled execution is immutable during runtime. The drained
751
+ -- protocol migration is the one bounded exception: prove the previous digest,
752
+ -- disable only that immutable-update trigger, replace the typed paths, and
753
+ -- recompute the database-native JSONB digest atomically.
754
+ ALTER TABLE scheduled_task_runs
755
+ DISABLE TRIGGER scheduled_task_run_connection_session_identity_immutable;
756
+
757
+ UPDATE scheduled_task_runs run
758
+ SET accepted_execution_snapshot = migrated.accepted_execution_snapshot,
759
+ accepted_execution_digest = encode(
760
+ digest(
761
+ convert_to(migrated.accepted_execution_snapshot::text, 'UTF8'),
762
+ 'sha256'
763
+ ),
764
+ 'hex'
765
+ )
766
+ FROM session_wait_scheduled_snapshot_cutover migrated
767
+ WHERE run.id = migrated.id;
768
+
769
+ ALTER TABLE scheduled_task_runs
770
+ ENABLE TRIGGER scheduled_task_run_connection_session_identity_immutable;
771
+
772
+ UPDATE workspace_packs pack
773
+ SET manifest = opengeni_private.session_wait_rewrite_pack(pack.manifest)
774
+ WHERE pack.manifest IS DISTINCT FROM
775
+ opengeni_private.session_wait_rewrite_pack(pack.manifest);
776
+
777
+ UPDATE pack_installations installation
778
+ SET manifest_snapshot = migrated.manifest_snapshot,
779
+ manifest_digest = migrated.manifest_digest
780
+ FROM session_wait_pack_snapshot_cutover migrated
781
+ WHERE installation.id = migrated.id;
782
+
783
+ DO $session_wait_selection_postcondition$
784
+ BEGIN
785
+ IF EXISTS (
786
+ SELECT 1 FROM sessions session
787
+ WHERE session.first_party_mcp_tools @> '["goal_wait"]'::jsonb
788
+ ) OR EXISTS (
789
+ SELECT 1 FROM workspaces workspace
790
+ WHERE workspace.settings #> '{sessionToolDefaults,firstPartyMcpTools}'
791
+ @> '["goal_wait"]'::jsonb
792
+ ) OR EXISTS (
793
+ SELECT 1 FROM new_session_drafts draft
794
+ WHERE draft.session_options -> 'firstPartyMcpTools'
795
+ @> '["goal_wait"]'::jsonb
796
+ ) OR EXISTS (
797
+ SELECT 1 FROM automation_trigger_revisions revision
798
+ WHERE revision.session_template -> 'firstPartyMcpTools'
799
+ @> '["goal_wait"]'::jsonb
800
+ ) OR EXISTS (
801
+ SELECT 1 FROM automation_runs run
802
+ WHERE run.accepted_execution #> '{sessionTemplate,firstPartyMcpTools}'
803
+ @> '["goal_wait"]'::jsonb
804
+ ) OR EXISTS (
805
+ SELECT 1 FROM scheduled_tasks task
806
+ WHERE task.agent_config IS DISTINCT FROM
807
+ opengeni_private.session_wait_rewrite_scheduled_agent_config(
808
+ task.agent_config
809
+ )
810
+ ) OR EXISTS (
811
+ SELECT 1 FROM scheduled_task_runs run
812
+ WHERE run.accepted_execution_snapshot -> 'resolvedFirstPartyMcpTools'
813
+ @> '["goal_wait"]'::jsonb
814
+ OR run.accepted_execution_snapshot
815
+ #> '{targetSessionExecution,firstPartyMcpTools}'
816
+ @> '["goal_wait"]'::jsonb
817
+ ) OR EXISTS (
818
+ SELECT 1 FROM workspace_packs pack
819
+ WHERE pack.manifest IS DISTINCT FROM
820
+ opengeni_private.session_wait_rewrite_pack(pack.manifest)
821
+ ) OR EXISTS (
822
+ SELECT 1 FROM pack_installations installation
823
+ WHERE installation.manifest_snapshot IS NOT NULL
824
+ AND installation.manifest_snapshot IS DISTINCT FROM
825
+ opengeni_private.session_wait_rewrite_pack(
826
+ installation.manifest_snapshot
827
+ )
828
+ ) THEN
829
+ RAISE EXCEPTION '0402 persisted first-party tool migration did not converge'
830
+ USING ERRCODE = '23514';
831
+ END IF;
832
+
833
+ IF EXISTS (
834
+ SELECT 1
835
+ FROM session_wait_scheduled_task_cutover migrated
836
+ JOIN scheduled_tasks task ON task.id = migrated.id
837
+ WHERE task.agent_config IS DISTINCT FROM migrated.agent_config
838
+ OR task.authority_revision IS DISTINCT FROM migrated.authority_revision
839
+ OR task.execution_digest IS DISTINCT FROM scheduled_task_execution_digest(task)
840
+ ) OR EXISTS (
841
+ SELECT 1
842
+ FROM session_wait_scheduled_task_cutover migrated
843
+ JOIN scheduled_tasks task ON task.id = migrated.id
844
+ JOIN scheduled_task_revision_authorities authority
845
+ ON authority.task_id = migrated.id
846
+ AND authority.task_authority_revision = migrated.authority_revision
847
+ WHERE authority.execution_digest IS DISTINCT FROM task.execution_digest
848
+ ) OR EXISTS (
849
+ SELECT 1
850
+ FROM session_wait_scheduled_task_cutover migrated
851
+ JOIN scheduled_tasks task ON task.id = migrated.id
852
+ JOIN scheduled_task_personal_resource_authorities authority
853
+ ON authority.task_id = migrated.id
854
+ AND authority.task_authority_revision = migrated.authority_revision
855
+ WHERE authority.execution_digest IS DISTINCT FROM task.execution_digest
856
+ ) OR EXISTS (
857
+ SELECT 1
858
+ FROM session_wait_scheduled_task_cutover migrated
859
+ JOIN scheduled_tasks task ON task.id = migrated.id
860
+ JOIN scheduled_task_connection_authority_snapshots snapshot
861
+ ON snapshot.task_id = migrated.id
862
+ AND snapshot.task_authority_revision = migrated.authority_revision
863
+ WHERE snapshot.execution_digest IS DISTINCT FROM task.execution_digest
864
+ OR snapshot.canonical_snapshot ->> 'executionDigest' IS DISTINCT FROM
865
+ task.execution_digest
866
+ OR snapshot.snapshot_digest IS DISTINCT FROM digest(
867
+ convert_to(snapshot.canonical_snapshot::text, 'UTF8'),
868
+ 'sha256'
869
+ )
870
+ ) THEN
871
+ RAISE EXCEPTION '0402 scheduled-task head migration did not converge'
872
+ USING ERRCODE = '23514';
873
+ END IF;
874
+
875
+ IF EXISTS (
876
+ SELECT 1
877
+ FROM session_wait_scheduled_snapshot_cutover migrated
878
+ JOIN scheduled_task_runs run ON run.id = migrated.id
879
+ WHERE run.accepted_execution_snapshot IS DISTINCT FROM
880
+ migrated.accepted_execution_snapshot
881
+ OR run.accepted_execution_digest IS DISTINCT FROM encode(
882
+ digest(
883
+ convert_to(migrated.accepted_execution_snapshot::text, 'UTF8'),
884
+ 'sha256'
885
+ ),
886
+ 'hex'
887
+ )
888
+ ) THEN
889
+ RAISE EXCEPTION '0402 scheduled accepted-execution migration did not converge'
890
+ USING ERRCODE = '23514';
891
+ END IF;
892
+
893
+ IF EXISTS (
894
+ SELECT 1
895
+ FROM session_wait_pack_snapshot_cutover migrated
896
+ JOIN pack_installations installation ON installation.id = migrated.id
897
+ WHERE installation.manifest_snapshot IS DISTINCT FROM migrated.manifest_snapshot
898
+ OR installation.manifest_digest IS DISTINCT FROM migrated.manifest_digest
899
+ ) THEN
900
+ RAISE EXCEPTION '0402 Pack manifest migration did not converge'
901
+ USING ERRCODE = '23514';
902
+ END IF;
903
+ END
904
+ $session_wait_selection_postcondition$;
905
+
906
+ -- The snapshot rewrite queues ordinary deferred row constraints. Prove them
907
+ -- before ALTER TABLE restores the production RLS posture.
908
+ SET CONSTRAINTS ALL IMMEDIATE;
909
+
910
+ ALTER TABLE "capability_operations" FORCE ROW LEVEL SECURITY;
911
+ ALTER TABLE "pack_installations" FORCE ROW LEVEL SECURITY;
912
+ ALTER TABLE "workspace_packs" FORCE ROW LEVEL SECURITY;
913
+ ALTER TABLE "scheduled_task_runs" FORCE ROW LEVEL SECURITY;
914
+ ALTER TABLE "scheduled_task_connection_authority_snapshots" FORCE ROW LEVEL SECURITY;
915
+ ALTER TABLE "scheduled_task_personal_resource_authorities" FORCE ROW LEVEL SECURITY;
916
+ ALTER TABLE "scheduled_task_revision_authorities" FORCE ROW LEVEL SECURITY;
917
+ ALTER TABLE "scheduled_tasks" FORCE ROW LEVEL SECURITY;
918
+ ALTER TABLE "automation_runs" FORCE ROW LEVEL SECURITY;
919
+ ALTER TABLE "automation_trigger_revisions" FORCE ROW LEVEL SECURITY;
920
+ ALTER TABLE "new_session_drafts" FORCE ROW LEVEL SECURITY;
921
+ ALTER TABLE "session_goals" FORCE ROW LEVEL SECURITY;
922
+ ALTER TABLE "sessions" FORCE ROW LEVEL SECURITY;
923
+
924
+ ALTER TABLE "sessions"
925
+ ADD CONSTRAINT "sessions_input_wait_check" CHECK (
926
+ (
927
+ "input_wait_turn_id" IS NULL
928
+ AND "input_wait_until" IS NULL
929
+ AND "input_wait_reason" IS NULL
930
+ AND "input_wait_set_at" IS NULL
931
+ )
932
+ OR (
933
+ "input_wait_turn_id" IS NOT NULL
934
+ AND "input_wait_until" IS NOT NULL
935
+ AND "input_wait_reason" IS NOT NULL
936
+ AND octet_length(btrim("input_wait_reason")) BETWEEN 1 AND 2048
937
+ AND "input_wait_set_at" IS NOT NULL
938
+ )
939
+ );
940
+
941
+ COMMENT ON COLUMN "sessions"."input_wait_turn_id" IS
942
+ 'Exact turn that declared wait_for_input; current only while it remains the latest finished turn.';
943
+ COMMENT ON COLUMN "sessions"."input_wait_until" IS
944
+ 'Absolute timeout safety-wake deadline persisted from wait_for_input timeoutSeconds.';
945
+ COMMENT ON COLUMN "sessions"."input_wait_reason" IS
946
+ 'Bounded agent-declared reason for the session-level wait.';
947
+ COMMENT ON COLUMN "sessions"."input_wait_set_at" IS
948
+ 'Database time when the current wait_for_input obligation was recorded.';
949
+
950
+ ALTER TABLE "session_goals"
951
+ DROP CONSTRAINT "session_goals_continuation_hold_check",
952
+ DROP COLUMN "continuation_hold_turn_id",
953
+ DROP COLUMN "continuation_hold_until",
954
+ DROP COLUMN "continuation_hold_reason",
955
+ DROP COLUMN "continuation_hold_set_at";
956
+
957
+ -- A wait operation belongs to the target session and logical turn, not to one
958
+ -- replaceable physical attempt. The receipt still retains the first attempt FK
959
+ -- for audit, while this partial identity makes a recovered attempt replay the
960
+ -- original deadline/result instead of stamping a second wait.
961
+ CREATE UNIQUE INDEX "session_command_receipts_wait_for_input_operation_uq"
962
+ ON "session_command_receipts" (
963
+ "workspace_id", "action", "target_session_id", "operation_key"
964
+ )
965
+ WHERE "action" = 'session.wait_for_input';
966
+
967
+ -- Terminal command settlement and wait timeouts are model-visible machine
968
+ -- inputs. Their discriminated payloads remain constrained exactly like every
969
+ -- other durable session system update.
970
+ ALTER TABLE "session_system_updates"
971
+ DROP CONSTRAINT "system_updates_kind_check";
972
+
973
+ ALTER TABLE "session_system_updates"
974
+ ADD CONSTRAINT "system_updates_kind_check" CHECK (
975
+ "kind" IN (
976
+ 'scheduled_occurrence',
977
+ 'goal_continuation',
978
+ 'agent_message',
979
+ 'agent_steer_instruction',
980
+ 'session_wait_timeout',
981
+ 'background_command_result',
982
+ 'child_terminal_result',
983
+ 'media_generation_result',
984
+ 'child_requires_action',
985
+ 'child_requires_action_resolved',
986
+ 'child_paused',
987
+ 'child_waiting_capacity',
988
+ 'child_progress'
989
+ )
990
+ );
991
+
992
+ DO $session_input_wait_runtime_drain_after$
993
+ DECLARE
994
+ configured_roles jsonb := current_setting(
995
+ 'opengeni.migration_application_roles',
996
+ true
997
+ )::jsonb;
998
+ BEGIN
999
+ IF EXISTS (
1000
+ SELECT 1 FROM pg_stat_activity activity
1001
+ JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
1002
+ ON roles.role_name = activity.usename
1003
+ WHERE activity.datname = current_database()
1004
+ AND activity.pid <> pg_backend_pid()
1005
+ )
1006
+ THEN
1007
+ RAISE EXCEPTION
1008
+ '0402 session input wait activation detected an application database session during cutover'
1009
+ USING ERRCODE = '55000';
1010
+ END IF;
1011
+ END
1012
+ $session_input_wait_runtime_drain_after$;
1013
+
1014
+ DROP FUNCTION opengeni_private.session_wait_rewrite_scheduled_execution(jsonb);
1015
+ DROP FUNCTION opengeni_private.session_wait_rewrite_scheduled_agent_config(jsonb);
1016
+ DROP FUNCTION opengeni_private.session_wait_rewrite_pack(jsonb);
1017
+ DROP FUNCTION opengeni_private.session_wait_rewrite_automation_template(jsonb);
1018
+ DROP FUNCTION opengeni_private.session_wait_translate_tool_array(jsonb);
1019
+ DROP FUNCTION opengeni_private.session_wait_canonical_json(jsonb);
1020
+ DROP COLLATION opengeni_private.session_wait_js_en_us;
1021
+
1022
+ RESET statement_timeout;
1023
+ RESET lock_timeout;