@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.
- package/dist/canonical-human-identities.js +3 -3
- package/dist/{chunk-DXJ2WY6W.js → chunk-53A2PEHT.js} +241 -31
- package/dist/chunk-53A2PEHT.js.map +1 -0
- package/dist/{chunk-JS3AWDKZ.js → chunk-5S4BQAHX.js} +3 -3
- package/dist/{chunk-JP6IYGYI.js → chunk-CITYUGMW.js} +3 -3
- package/dist/{chunk-6FIVOJ3V.js → chunk-F5YOZEYQ.js} +45 -1
- package/dist/chunk-F5YOZEYQ.js.map +1 -0
- package/dist/{chunk-ZTSJPFDH.js → chunk-HZV7O6JR.js} +2 -2
- package/dist/{chunk-VDXHLVDE.js → chunk-RLQVPOVR.js} +2 -2
- package/dist/{chunk-LHKB62GC.js → chunk-UKOGHUGC.js} +2 -2
- package/dist/{chunk-QL4AHBYD.js → chunk-X6TO5HII.js} +2 -2
- package/dist/{chunk-3ZMN4EXC.js → chunk-Y53WEFYB.js} +2 -2
- package/dist/connection-token-resolver.d.ts +4 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +59 -12
- package/dist/index.js +5259 -4374
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +9 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/new-session-drafts.d.ts +12 -0
- package/dist/organization-membership-lifecycle.d.ts +8 -1
- package/dist/organization-model-providers.d.ts +101 -0
- package/dist/pack-components.d.ts +1 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +5 -5
- package/dist/schema.d.ts +645 -0
- package/dist/schema.js +7 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/drizzle/0390_organization_model_provider_connections.sql +197 -0
- package/drizzle/0391_sandbox_provider_deadline_interaction_followup.sql +295 -0
- package/drizzle/0392_context_compaction_pending_observability.sql +198 -0
- package/drizzle/0393_workspace_memory_and_learning_defaults.sql +102 -0
- package/drizzle/0394_session_selected_skill_activation.sql +20 -0
- package/drizzle/0395_scheduled_task_unclaimed_occurrence_invalidation.sql +154 -0
- package/drizzle/0396_model_call_equivalent_credit_cost.sql +28 -0
- package/drizzle/0397_sandbox_deadline_rotation_preemption.sql +113 -0
- package/drizzle/0398_organization_workspace_management_entry.sql +206 -0
- package/drizzle/0399_additional_managed_organization_creation.sql +691 -0
- package/package.json +6 -6
- package/src/connection-token-resolver.ts +12 -0
- package/src/index.ts +701 -155
- package/src/insights-model-bundle.ts +54 -5
- package/src/insights.ts +32 -0
- package/src/new-session-drafts.ts +66 -21
- package/src/organization-membership-lifecycle.ts +47 -5
- package/src/organization-model-providers.ts +614 -0
- package/src/pack-components.ts +15 -2
- package/src/provision-roles.ts +2 -0
- package/src/runtime-posture.ts +44 -0
- package/src/schema.ts +250 -30
- package/dist/chunk-6FIVOJ3V.js.map +0 -1
- package/dist/chunk-DXJ2WY6W.js.map +0 -1
- /package/dist/{chunk-JS3AWDKZ.js.map → chunk-5S4BQAHX.js.map} +0 -0
- /package/dist/{chunk-JP6IYGYI.js.map → chunk-CITYUGMW.js.map} +0 -0
- /package/dist/{chunk-ZTSJPFDH.js.map → chunk-HZV7O6JR.js.map} +0 -0
- /package/dist/{chunk-VDXHLVDE.js.map → chunk-RLQVPOVR.js.map} +0 -0
- /package/dist/{chunk-LHKB62GC.js.map → chunk-UKOGHUGC.js.map} +0 -0
- /package/dist/{chunk-QL4AHBYD.js.map → chunk-X6TO5HII.js.map} +0 -0
- /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;
|