@opengeni/db 3.7.4-canary.0 → 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 (62) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/{chunk-DXJ2WY6W.js → chunk-53A2PEHT.js} +241 -31
  3. package/dist/chunk-53A2PEHT.js.map +1 -0
  4. package/dist/{chunk-JS3AWDKZ.js → chunk-5S4BQAHX.js} +3 -3
  5. package/dist/{chunk-JP6IYGYI.js → chunk-CITYUGMW.js} +3 -3
  6. package/dist/{chunk-6FIVOJ3V.js → chunk-F5YOZEYQ.js} +45 -1
  7. package/dist/chunk-F5YOZEYQ.js.map +1 -0
  8. package/dist/{chunk-ZTSJPFDH.js → chunk-HZV7O6JR.js} +2 -2
  9. package/dist/{chunk-VDXHLVDE.js → chunk-RLQVPOVR.js} +2 -2
  10. package/dist/{chunk-LHKB62GC.js → chunk-UKOGHUGC.js} +2 -2
  11. package/dist/{chunk-QL4AHBYD.js → chunk-X6TO5HII.js} +2 -2
  12. package/dist/{chunk-3ZMN4EXC.js → chunk-Y53WEFYB.js} +2 -2
  13. package/dist/connection-token-resolver.d.ts +4 -0
  14. package/dist/editable-artifact-durable-export.js +2 -2
  15. package/dist/editable-artifacts.js +3 -3
  16. package/dist/index.d.ts +59 -12
  17. package/dist/index.js +5259 -4374
  18. package/dist/index.js.map +1 -1
  19. package/dist/insights.d.ts +9 -0
  20. package/dist/managed-auth-session-sets.js +3 -3
  21. package/dist/new-session-drafts.d.ts +12 -0
  22. package/dist/organization-membership-lifecycle.d.ts +8 -1
  23. package/dist/organization-model-providers.d.ts +101 -0
  24. package/dist/pack-components.d.ts +1 -0
  25. package/dist/provision-roles.js +1 -1
  26. package/dist/runtime-posture.d.ts +5 -5
  27. package/dist/schema.d.ts +645 -0
  28. package/dist/schema.js +7 -1
  29. package/dist/session-background-commands.js +3 -3
  30. package/dist/session-tenancy.js +3 -3
  31. package/dist/video-generation.js +3 -3
  32. package/drizzle/0390_organization_model_provider_connections.sql +197 -0
  33. package/drizzle/0391_sandbox_provider_deadline_interaction_followup.sql +295 -0
  34. package/drizzle/0392_context_compaction_pending_observability.sql +198 -0
  35. package/drizzle/0393_workspace_memory_and_learning_defaults.sql +102 -0
  36. package/drizzle/0394_session_selected_skill_activation.sql +20 -0
  37. package/drizzle/0395_scheduled_task_unclaimed_occurrence_invalidation.sql +154 -0
  38. package/drizzle/0396_model_call_equivalent_credit_cost.sql +28 -0
  39. package/drizzle/0397_sandbox_deadline_rotation_preemption.sql +113 -0
  40. package/drizzle/0398_organization_workspace_management_entry.sql +206 -0
  41. package/drizzle/0399_additional_managed_organization_creation.sql +691 -0
  42. package/package.json +6 -6
  43. package/src/connection-token-resolver.ts +12 -0
  44. package/src/index.ts +701 -155
  45. package/src/insights-model-bundle.ts +54 -5
  46. package/src/insights.ts +32 -0
  47. package/src/new-session-drafts.ts +66 -21
  48. package/src/organization-membership-lifecycle.ts +47 -5
  49. package/src/organization-model-providers.ts +614 -0
  50. package/src/pack-components.ts +15 -2
  51. package/src/provision-roles.ts +2 -0
  52. package/src/runtime-posture.ts +44 -0
  53. package/src/schema.ts +250 -30
  54. package/dist/chunk-6FIVOJ3V.js.map +0 -1
  55. package/dist/chunk-DXJ2WY6W.js.map +0 -1
  56. /package/dist/{chunk-JS3AWDKZ.js.map → chunk-5S4BQAHX.js.map} +0 -0
  57. /package/dist/{chunk-JP6IYGYI.js.map → chunk-CITYUGMW.js.map} +0 -0
  58. /package/dist/{chunk-ZTSJPFDH.js.map → chunk-HZV7O6JR.js.map} +0 -0
  59. /package/dist/{chunk-VDXHLVDE.js.map → chunk-RLQVPOVR.js.map} +0 -0
  60. /package/dist/{chunk-LHKB62GC.js.map → chunk-UKOGHUGC.js.map} +0 -0
  61. /package/dist/{chunk-QL4AHBYD.js.map → chunk-X6TO5HII.js.map} +0 -0
  62. /package/dist/{chunk-3ZMN4EXC.js.map → chunk-Y53WEFYB.js.map} +0 -0
@@ -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;
@@ -0,0 +1,154 @@
1
+ -- deployment-mode: rolling
2
+ -- Pausing or soft-deleting a scheduled task is a durable first-claim cutoff.
3
+ -- Runs whose scheduler-owned turn already exists remain accepted work and keep
4
+ -- their normal attempt/recovery lifecycle. Every earlier occurrence becomes a
5
+ -- terminal receipt, so a later Resume cannot revive a deposit accepted before
6
+ -- the lifecycle boundary.
7
+
8
+ SET LOCAL lock_timeout = '5s';
9
+ SET LOCAL statement_timeout = '5min';
10
+
11
+ -- Old claimers do not inspect scheduled_task_runs.status after this migration
12
+ -- marks an occurrence terminal. Keep the rolling boundary safe in the database:
13
+ -- every binary must cross this state transition before it can create the
14
+ -- scheduler-owned turn or start provider work.
15
+ CREATE FUNCTION fence_terminal_scheduled_occurrence_delivery()
16
+ RETURNS trigger
17
+ LANGUAGE plpgsql
18
+ SECURITY DEFINER
19
+ SET search_path FROM CURRENT
20
+ AS $body$
21
+ DECLARE
22
+ run_status text;
23
+ BEGIN
24
+ SELECT run.status INTO run_status
25
+ FROM scheduled_task_runs run
26
+ WHERE run.id = OLD.scheduled_task_run_id
27
+ AND run.account_id = OLD.account_id
28
+ AND run.workspace_id = OLD.workspace_id
29
+ AND run.session_id = OLD.session_id
30
+ AND run.action_kind = 'agent_turn'
31
+ FOR UPDATE;
32
+
33
+ IF run_status IS DISTINCT FROM 'dispatched' THEN
34
+ RAISE EXCEPTION 'terminal scheduled occurrence cannot be delivered'
35
+ USING ERRCODE = '42501';
36
+ END IF;
37
+ RETURN NEW;
38
+ END
39
+ $body$;
40
+
41
+ CREATE TRIGGER scheduled_terminal_occurrence_delivery_fence
42
+ BEFORE UPDATE OF state ON session_system_updates
43
+ FOR EACH ROW
44
+ WHEN (
45
+ OLD.scheduled_task_run_id IS NOT NULL
46
+ AND OLD.state = 'pending'
47
+ AND NEW.state = 'delivered'
48
+ )
49
+ EXECUTE FUNCTION fence_terminal_scheduled_occurrence_delivery();
50
+
51
+ CREATE FUNCTION invalidate_unclaimed_scheduled_agent_runs_on_task_inactive()
52
+ RETURNS trigger
53
+ LANGUAGE plpgsql
54
+ SECURITY DEFINER
55
+ SET search_path FROM CURRENT
56
+ AS $body$
57
+ DECLARE
58
+ run_row record;
59
+ invalidation_error text;
60
+ BEGIN
61
+ invalidation_error := CASE
62
+ -- 0275 introduced deleted_at. JSON row introspection keeps this rolling
63
+ -- trigger installable in legacy-upgrade fixtures that intentionally replay
64
+ -- 0275 after the current tail, while current schemas still classify delete.
65
+ WHEN pg_catalog.to_jsonb(OLD) ->> 'deleted_at' IS NULL
66
+ AND pg_catalog.to_jsonb(NEW) ->> 'deleted_at' IS NOT NULL
67
+ THEN 'scheduled_task_deleted_before_claim'
68
+ ELSE 'scheduled_task_paused_before_claim'
69
+ END;
70
+
71
+ INSERT INTO opengeni_private.scheduled_personal_resource_capabilities (
72
+ backend_pid, transaction_id, capability_kind
73
+ ) VALUES (
74
+ pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'run_lifecycle'
75
+ )
76
+ ON CONFLICT DO NOTHING;
77
+
78
+ -- Run admission takes the task row first. Deposit and claim both lock the run
79
+ -- before they can publish an occurrence or a scheduler-owned turn. Once this
80
+ -- loop owns a run, a fresh turn lookup therefore decides the race exactly:
81
+ -- no turn means no claim crossed the lifecycle boundary.
82
+ FOR run_row IN
83
+ SELECT run.id
84
+ FROM scheduled_task_runs run
85
+ WHERE run.account_id = NEW.account_id
86
+ AND run.workspace_id = NEW.workspace_id
87
+ AND run.task_id = NEW.id
88
+ AND run.action_kind = 'agent_turn'
89
+ AND run.status IN ('queued', 'dispatched')
90
+ ORDER BY run.id
91
+ FOR UPDATE
92
+ LOOP
93
+ IF NOT EXISTS (
94
+ SELECT 1
95
+ FROM session_turns turn_value
96
+ WHERE turn_value.account_id = NEW.account_id
97
+ AND turn_value.workspace_id = NEW.workspace_id
98
+ AND turn_value.scheduled_task_run_id = run_row.id
99
+ ) THEN
100
+ UPDATE scheduled_task_runs run
101
+ SET status = 'skipped',
102
+ error = invalidation_error,
103
+ completed_at = pg_catalog.clock_timestamp(),
104
+ updated_at = pg_catalog.clock_timestamp()
105
+ WHERE run.id = run_row.id
106
+ AND run.account_id = NEW.account_id
107
+ AND run.workspace_id = NEW.workspace_id
108
+ AND run.status IN ('queued', 'dispatched');
109
+ END IF;
110
+ END LOOP;
111
+
112
+ DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
113
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
114
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
115
+ AND capability_kind = 'run_lifecycle';
116
+ RETURN NEW;
117
+ EXCEPTION WHEN OTHERS THEN
118
+ DELETE FROM opengeni_private.scheduled_personal_resource_capabilities
119
+ WHERE backend_pid = pg_catalog.pg_backend_pid()
120
+ AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
121
+ AND capability_kind = 'run_lifecycle';
122
+ RAISE;
123
+ END
124
+ $body$;
125
+
126
+ CREATE TRIGGER scheduled_task_unclaimed_occurrence_invalidation
127
+ AFTER UPDATE OF status ON scheduled_tasks
128
+ FOR EACH ROW
129
+ WHEN (OLD.status = 'active' AND NEW.status = 'paused')
130
+ EXECUTE FUNCTION invalidate_unclaimed_scheduled_agent_runs_on_task_inactive();
131
+
132
+ REVOKE ALL ON FUNCTION
133
+ invalidate_unclaimed_scheduled_agent_runs_on_task_inactive()
134
+ FROM PUBLIC;
135
+ REVOKE ALL ON FUNCTION fence_terminal_scheduled_occurrence_delivery() FROM PUBLIC;
136
+
137
+ DO $pin_scheduled_task_unclaimed_invalidation$
138
+ DECLARE
139
+ data_schema text := pg_catalog.current_schema();
140
+ BEGIN
141
+ EXECUTE pg_catalog.format(
142
+ 'ALTER FUNCTION %I.invalidate_unclaimed_scheduled_agent_runs_on_task_inactive() '
143
+ || 'SET search_path = pg_catalog, %I, pg_temp',
144
+ data_schema,
145
+ data_schema
146
+ );
147
+ EXECUTE pg_catalog.format(
148
+ 'ALTER FUNCTION %I.fence_terminal_scheduled_occurrence_delivery() '
149
+ || 'SET search_path = pg_catalog, %I, pg_temp',
150
+ data_schema,
151
+ data_schema
152
+ );
153
+ END
154
+ $pin_scheduled_task_unclaimed_invalidation$;
@@ -0,0 +1,28 @@
1
+ -- deployment-mode: rolling
2
+ -- Preserve the hypothetical OpenGeni credit price separately from both the
3
+ -- upstream provider estimate and the amount actually priced to credits.
4
+ -- Historical external facts remain unknown rather than being recomputed with
5
+ -- today's margin or provider rates.
6
+
7
+ SET LOCAL lock_timeout = '5s';
8
+ SET LOCAL statement_timeout = '5min';
9
+
10
+ ALTER TABLE "model_call_facts"
11
+ ADD COLUMN IF NOT EXISTS "equivalent_credit_cost_micros" bigint;
12
+
13
+ ALTER TABLE "model_call_facts"
14
+ DROP CONSTRAINT IF EXISTS "model_call_facts_equivalent_credit_cost_check",
15
+ ADD CONSTRAINT "model_call_facts_equivalent_credit_cost_check"
16
+ CHECK (
17
+ "equivalent_credit_cost_micros" IS NULL
18
+ OR (
19
+ "equivalent_credit_cost_micros" >= 0
20
+ AND "estimated_provider_cost_micros" IS NOT NULL
21
+ )
22
+ ) NOT VALID;
23
+
24
+ ALTER TABLE "model_call_facts"
25
+ VALIDATE CONSTRAINT "model_call_facts_equivalent_credit_cost_check";
26
+
27
+ RESET statement_timeout;
28
+ RESET lock_timeout;
@@ -0,0 +1,113 @@
1
+ -- deployment-mode: rolling
2
+ -- A finite provider lifetime is a hard destruction boundary. Once the normal
3
+ -- lead-time rotation request is durable, persistent Browser/Computer placement
4
+ -- must yield while enough time remains to quiesce writers and capture the exact
5
+ -- workspace generation. Waiting until the provider deadline keeps the lease
6
+ -- holder alive until the same instant the provider may destroy the box.
7
+ SET LOCAL lock_timeout = '5s';
8
+ SET LOCAL statement_timeout = '10min';
9
+
10
+ DO $sandbox_deadline_rotation_preemption$
11
+ DECLARE
12
+ definition text;
13
+ patched text;
14
+ eligibility_anchor constant text :=
15
+ E' AND lease.provider_deadline_at <= pg_catalog.now()';
16
+ eligibility_replacement constant text :=
17
+ E' -- 0397 deadline rotation preemption: an automatic finite-lifetime\n'
18
+ || E' -- rotation yields persistent controllers at the lead boundary;\n'
19
+ || E' -- unrelated operator rotations retain the hard-deadline fallback.\n'
20
+ || E' AND (\n'
21
+ || E' lease.rotation_reason = ''provider_deadline''\n'
22
+ || E' OR lease.provider_deadline_at <= pg_catalog.now()\n'
23
+ || E' )';
24
+ message_anchor constant text :=
25
+ E' error_message = ''Interaction controller reached its sandbox provider deadline'',';
26
+ message_replacement constant text :=
27
+ E' error_message = ''Interaction controller yielded for sandbox provider rotation'',';
28
+ occurrences integer;
29
+ BEGIN
30
+ SELECT pg_catalog.pg_get_functiondef(
31
+ 'opengeni_private.reap_stale_interaction_transitions(bigint)'::regprocedure
32
+ ) INTO definition;
33
+
34
+ IF definition IS NULL
35
+ OR pg_catalog.strpos(
36
+ definition,
37
+ '0391 provider-deadline interaction follow-up'
38
+ ) = 0
39
+ OR pg_catalog.strpos(
40
+ definition,
41
+ 'interaction_holder.kind = ''interaction'''
42
+ ) = 0
43
+ OR pg_catalog.strpos(definition, 'provider_deadline_rotation') = 0
44
+ THEN
45
+ RAISE EXCEPTION '0397 interaction rotation prerequisite definition drift'
46
+ USING ERRCODE = '55000';
47
+ END IF;
48
+
49
+ IF pg_catalog.strpos(definition, '0397 deadline rotation preemption') > 0 THEN
50
+ IF pg_catalog.strpos(
51
+ definition,
52
+ 'lease.rotation_reason = ''provider_deadline'''
53
+ ) = 0
54
+ OR pg_catalog.strpos(
55
+ definition,
56
+ 'Interaction controller yielded for sandbox provider rotation'
57
+ ) = 0
58
+ THEN
59
+ RAISE EXCEPTION '0397 interaction rotation replay definition drift'
60
+ USING ERRCODE = '55000';
61
+ END IF;
62
+ ELSE
63
+ patched := definition;
64
+
65
+ occurrences := (
66
+ pg_catalog.length(patched)
67
+ - pg_catalog.length(pg_catalog.replace(patched, eligibility_anchor, ''))
68
+ ) / pg_catalog.length(eligibility_anchor);
69
+ IF occurrences <> 1 THEN
70
+ RAISE EXCEPTION '0397 interaction rotation eligibility anchor drift'
71
+ USING ERRCODE = '55000';
72
+ END IF;
73
+ patched := pg_catalog.replace(
74
+ patched,
75
+ eligibility_anchor,
76
+ eligibility_replacement
77
+ );
78
+
79
+ occurrences := (
80
+ pg_catalog.length(patched)
81
+ - pg_catalog.length(pg_catalog.replace(patched, message_anchor, ''))
82
+ ) / pg_catalog.length(message_anchor);
83
+ IF occurrences <> 1 THEN
84
+ RAISE EXCEPTION '0397 interaction rotation message anchor drift'
85
+ USING ERRCODE = '55000';
86
+ END IF;
87
+ patched := pg_catalog.replace(
88
+ patched,
89
+ message_anchor,
90
+ message_replacement
91
+ );
92
+
93
+ IF pg_catalog.strpos(patched, '0397 deadline rotation preemption') = 0
94
+ OR pg_catalog.strpos(
95
+ patched,
96
+ 'lease.rotation_reason = ''provider_deadline'''
97
+ ) = 0
98
+ OR pg_catalog.strpos(
99
+ patched,
100
+ 'OR lease.provider_deadline_at <= pg_catalog.now()'
101
+ ) = 0
102
+ THEN
103
+ RAISE EXCEPTION '0397 interaction rotation patch failed'
104
+ USING ERRCODE = '55000';
105
+ END IF;
106
+
107
+ EXECUTE patched;
108
+ END IF;
109
+ END
110
+ $sandbox_deadline_rotation_preemption$;
111
+
112
+ RESET statement_timeout;
113
+ RESET lock_timeout;