@opengeni/db 0.6.1 → 0.7.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/{chunk-OGCE6O2X.js → chunk-7LDU7F5P.js} +31 -3
- package/dist/chunk-7LDU7F5P.js.map +1 -0
- package/dist/chunk-O3D7DABC.js +2314 -0
- package/dist/chunk-O3D7DABC.js.map +1 -0
- package/dist/{chunk-57MLICFR.js → chunk-YFQ7SGE4.js} +18 -6
- package/dist/chunk-YFQ7SGE4.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +13137 -3518
- package/dist/index.js.map +1 -1
- package/dist/migrate.js +1 -1
- package/dist/provision-roles.d.ts +1831 -222
- package/dist/provision-roles.js +1 -1
- package/dist/{schema-BUbuMteO.d.ts → schema-Dp2MbBxx.d.ts} +7477 -3302
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +33 -5
- package/drizzle/0044_reap_dead_turn_holders.sql +104 -0
- package/drizzle/0045_workspace_captures.sql +83 -0
- package/drizzle/0045_workspace_memory_v1.sql +71 -0
- package/drizzle/0046_variable_sets_rename.sql +56 -0
- package/drizzle/0047_rigs.sql +151 -0
- package/drizzle/0048_rig_runtime.sql +9 -0
- package/drizzle/0049_enrollment_went_offline.sql +28 -0
- package/drizzle/0050_enrollment_op_stream.sql +1 -0
- package/drizzle/0051_codex_pin_source.sql +48 -0
- package/drizzle/0052_file_upload_cleanup.sql +91 -0
- package/drizzle/0053_codex_credential_leases.sql +230 -0
- package/drizzle/0054_session_pins.sql +85 -0
- package/drizzle/0055_session_list_snapshots.sql +73 -0
- package/drizzle/0056_workspace_model_policies.sql +48 -0
- package/drizzle/0057_durable_queue_control.sql +536 -0
- package/drizzle/0058_turn_admission_usage_enrollment.sql +158 -0
- package/drizzle/0059_workspace_pause_control_kind.sql +12 -0
- package/drizzle/0060_session_system_update_deferral.sql +10 -0
- package/drizzle/0061_session_workflow_wake_outbox.sql +157 -0
- package/drizzle/0062_session_list_snapshot_reaper.sql +48 -0
- package/package.json +17 -13
- package/src/codex-token-resolver.ts +58 -23
- package/src/connection-token-resolver.ts +146 -57
- package/src/environment-crypto.ts +5 -1
- package/src/event-payload-sanitizer.ts +29 -1
- package/src/index.ts +22428 -6479
- package/src/memory-domain.ts +218 -0
- package/src/migrate.ts +58 -3
- package/src/provision-roles.ts +46 -17
- package/src/schema.ts +2557 -1121
- package/src/session-control-cutover-audit.ts +1203 -0
- package/dist/chunk-57MLICFR.js.map +0 -1
- package/dist/chunk-OGCE6O2X.js.map +0 -1
- package/dist/chunk-ZIUCA2IO.js +0 -1268
- package/dist/chunk-ZIUCA2IO.js.map +0 -1
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
-- OPE-18/OPE-21/OPE-22: one prompt queue, one current inference, portable
|
|
2
|
+
-- compaction, attempt-fenced publication, coalesced internal updates, and
|
|
3
|
+
-- session/workspace Pause.
|
|
4
|
+
--
|
|
5
|
+
-- This is a one-way maintenance migration. The production release gate stops
|
|
6
|
+
-- admission, pauses Temporal Schedules, drains workers, terminates the old
|
|
7
|
+
-- session workflows, and scales the old worker fleet to zero before applying
|
|
8
|
+
-- it. No old worker or old workflow is allowed to execute after this boundary.
|
|
9
|
+
|
|
10
|
+
ALTER TABLE "workspaces" ADD COLUMN "inference_state" text NOT NULL DEFAULT 'active';
|
|
11
|
+
ALTER TABLE "workspaces" ADD COLUMN "inference_generation" integer NOT NULL DEFAULT 0;
|
|
12
|
+
ALTER TABLE "workspaces" ADD COLUMN "inference_reason" text;
|
|
13
|
+
ALTER TABLE "workspaces" ADD COLUMN "inference_changed_by" text;
|
|
14
|
+
ALTER TABLE "workspaces" ADD COLUMN "inference_changed_at" timestamptz;
|
|
15
|
+
|
|
16
|
+
ALTER TABLE "sessions" ADD COLUMN "queue_version" integer NOT NULL DEFAULT 0;
|
|
17
|
+
ALTER TABLE "sessions" ADD COLUMN "queue_head_position" bigint NOT NULL DEFAULT 0;
|
|
18
|
+
ALTER TABLE "sessions" ADD COLUMN "queue_tail_position" bigint NOT NULL DEFAULT 0;
|
|
19
|
+
ALTER TABLE "sessions" ADD COLUMN "control_state" text NOT NULL DEFAULT 'active';
|
|
20
|
+
ALTER TABLE "sessions" ADD COLUMN "control_generation" integer NOT NULL DEFAULT 0;
|
|
21
|
+
ALTER TABLE "sessions" ADD COLUMN "control_reason" text;
|
|
22
|
+
ALTER TABLE "sessions" ADD COLUMN "control_changed_by" text;
|
|
23
|
+
ALTER TABLE "sessions" ADD COLUMN "control_changed_at" timestamptz;
|
|
24
|
+
ALTER TABLE "sessions" ADD COLUMN "pending_control_event_id" uuid;
|
|
25
|
+
ALTER TABLE "sessions" ADD COLUMN "pending_control_kind" text;
|
|
26
|
+
ALTER TABLE "sessions" ADD COLUMN "pending_control_expected_turn_id" uuid;
|
|
27
|
+
ALTER TABLE "sessions" ADD COLUMN "pending_control_expected_generation" integer;
|
|
28
|
+
ALTER TABLE "sessions" ADD COLUMN "pending_control_expected_attempt_id" uuid;
|
|
29
|
+
ALTER TABLE "sessions" ADD COLUMN "workspace_run_exception_generation" integer;
|
|
30
|
+
|
|
31
|
+
ALTER TABLE "session_turns" ALTER COLUMN "position" TYPE bigint USING "position"::bigint;
|
|
32
|
+
ALTER TABLE "session_turns" ADD COLUMN "version" integer NOT NULL DEFAULT 1;
|
|
33
|
+
ALTER TABLE "session_turns" ADD COLUMN "execution_generation" integer NOT NULL DEFAULT 0;
|
|
34
|
+
ALTER TABLE "session_turns" ADD COLUMN "active_attempt_id" uuid;
|
|
35
|
+
ALTER TABLE "session_turns" ADD COLUMN "lineage" jsonb NOT NULL DEFAULT '{}'::jsonb;
|
|
36
|
+
ALTER TABLE "session_turns" ADD COLUMN "cancelled_by" text;
|
|
37
|
+
ALTER TABLE "session_turns" ADD COLUMN "cancel_reason" text;
|
|
38
|
+
|
|
39
|
+
ALTER TABLE "session_events" ADD COLUMN "turn_generation" integer;
|
|
40
|
+
ALTER TABLE "session_events" ADD COLUMN "turn_attempt_id" uuid;
|
|
41
|
+
ALTER TABLE "session_events" ADD COLUMN "turn_association" text;
|
|
42
|
+
|
|
43
|
+
ALTER TABLE "scheduled_task_runs" ADD COLUMN "producer_key" text;
|
|
44
|
+
|
|
45
|
+
CREATE TABLE "session_system_updates" (
|
|
46
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
47
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
48
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
49
|
+
"session_id" uuid NOT NULL,
|
|
50
|
+
"kind" text NOT NULL,
|
|
51
|
+
"classification" text NOT NULL DEFAULT 'info',
|
|
52
|
+
"source_id" text NOT NULL,
|
|
53
|
+
"dedupe_key" text NOT NULL,
|
|
54
|
+
"summary" text NOT NULL,
|
|
55
|
+
"payload" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
56
|
+
"lineage" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
57
|
+
"state" text NOT NULL DEFAULT 'pending',
|
|
58
|
+
"delivered_turn_id" uuid,
|
|
59
|
+
"delivered_at" timestamptz,
|
|
60
|
+
"created_at" timestamptz NOT NULL DEFAULT now()
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
-- Capacity becoming available is an internal update, never a queued goal turn.
|
|
64
|
+
-- Retarget the durable waiter receipt from the removed queue representation to
|
|
65
|
+
-- the typed update that will drive the next internal inference.
|
|
66
|
+
ALTER TABLE "codex_capacity_waiters"
|
|
67
|
+
DROP CONSTRAINT "codex_capacity_waiters_workspace_resumed_turn_fk";
|
|
68
|
+
ALTER TABLE "codex_capacity_waiters"
|
|
69
|
+
RENAME COLUMN "resumed_turn_id" TO "resumed_update_id";
|
|
70
|
+
UPDATE "codex_capacity_waiters" SET "resumed_update_id" = NULL;
|
|
71
|
+
|
|
72
|
+
CREATE TABLE "runtime_control_operations" (
|
|
73
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
74
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
75
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
76
|
+
"scope" text NOT NULL,
|
|
77
|
+
"target_id" uuid NOT NULL,
|
|
78
|
+
"client_event_id" text NOT NULL,
|
|
79
|
+
"requested_state" text NOT NULL,
|
|
80
|
+
"expected_state" text,
|
|
81
|
+
"expected_generation" integer,
|
|
82
|
+
"result" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
83
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
84
|
+
"updated_at" timestamptz NOT NULL DEFAULT now()
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
CREATE TABLE "session_system_update_outbox" (
|
|
88
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
89
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
90
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
91
|
+
"source_session_id" uuid NOT NULL,
|
|
92
|
+
"target_session_id" uuid NOT NULL,
|
|
93
|
+
"dedupe_key" text NOT NULL,
|
|
94
|
+
"kind" text NOT NULL,
|
|
95
|
+
"classification" text NOT NULL,
|
|
96
|
+
"source_id" text NOT NULL,
|
|
97
|
+
"summary" text NOT NULL,
|
|
98
|
+
"payload" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
99
|
+
"lineage" jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
100
|
+
"status" text NOT NULL DEFAULT 'pending',
|
|
101
|
+
"attempts" integer NOT NULL DEFAULT 0,
|
|
102
|
+
"update_id" uuid,
|
|
103
|
+
"last_error" text,
|
|
104
|
+
"delivered_at" timestamptz,
|
|
105
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
106
|
+
"updated_at" timestamptz NOT NULL DEFAULT now()
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
CREATE TABLE "session_pending_tool_calls" (
|
|
110
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
111
|
+
"account_id" uuid NOT NULL REFERENCES "managed_accounts"("id") ON DELETE CASCADE,
|
|
112
|
+
"workspace_id" uuid NOT NULL REFERENCES "workspaces"("id") ON DELETE CASCADE,
|
|
113
|
+
"session_id" uuid NOT NULL,
|
|
114
|
+
"turn_id" uuid NOT NULL,
|
|
115
|
+
"execution_generation" integer NOT NULL,
|
|
116
|
+
"attempt_id" uuid NOT NULL,
|
|
117
|
+
"call_id" text NOT NULL,
|
|
118
|
+
"call_type" text NOT NULL,
|
|
119
|
+
"call_item" jsonb NOT NULL,
|
|
120
|
+
"result_item" jsonb,
|
|
121
|
+
"result_recorded_at" timestamptz,
|
|
122
|
+
"created_at" timestamptz NOT NULL DEFAULT now()
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
-- Composite identities make every new foreign key workspace-scoped.
|
|
126
|
+
CREATE UNIQUE INDEX "sessions_workspace_id_uq" ON "sessions" ("workspace_id", "id");
|
|
127
|
+
CREATE UNIQUE INDEX "session_turns_workspace_id_uq" ON "session_turns" ("workspace_id", "id");
|
|
128
|
+
CREATE UNIQUE INDEX "session_events_workspace_id_uq" ON "session_events" ("workspace_id", "id");
|
|
129
|
+
CREATE UNIQUE INDEX "session_system_updates_workspace_id_uq"
|
|
130
|
+
ON "session_system_updates" ("workspace_id", "id");
|
|
131
|
+
|
|
132
|
+
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_pending_control_event_fk"
|
|
133
|
+
FOREIGN KEY ("workspace_id", "pending_control_event_id")
|
|
134
|
+
REFERENCES "session_events"("workspace_id", "id") ON DELETE SET NULL;
|
|
135
|
+
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_pending_control_turn_fk"
|
|
136
|
+
FOREIGN KEY ("workspace_id", "pending_control_expected_turn_id")
|
|
137
|
+
REFERENCES "session_turns"("workspace_id", "id") ON DELETE SET NULL;
|
|
138
|
+
ALTER TABLE "session_system_updates" ADD CONSTRAINT "system_updates_workspace_account_fk"
|
|
139
|
+
FOREIGN KEY ("workspace_id", "account_id")
|
|
140
|
+
REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
|
|
141
|
+
ALTER TABLE "session_system_updates" ADD CONSTRAINT "system_updates_workspace_session_fk"
|
|
142
|
+
FOREIGN KEY ("workspace_id", "session_id")
|
|
143
|
+
REFERENCES "sessions"("workspace_id", "id") ON DELETE CASCADE;
|
|
144
|
+
ALTER TABLE "session_system_updates" ADD CONSTRAINT "system_updates_workspace_turn_fk"
|
|
145
|
+
FOREIGN KEY ("workspace_id", "delivered_turn_id")
|
|
146
|
+
REFERENCES "session_turns"("workspace_id", "id") ON DELETE SET NULL
|
|
147
|
+
DEFERRABLE INITIALLY DEFERRED;
|
|
148
|
+
ALTER TABLE "codex_capacity_waiters" ADD CONSTRAINT "codex_capacity_waiters_workspace_resumed_update_fk"
|
|
149
|
+
FOREIGN KEY ("workspace_id", "resumed_update_id")
|
|
150
|
+
REFERENCES "session_system_updates"("workspace_id", "id") ON DELETE SET NULL;
|
|
151
|
+
ALTER TABLE "runtime_control_operations" ADD CONSTRAINT "runtime_control_operations_workspace_account_fk"
|
|
152
|
+
FOREIGN KEY ("workspace_id", "account_id")
|
|
153
|
+
REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
|
|
154
|
+
ALTER TABLE "session_system_update_outbox" ADD CONSTRAINT "system_update_outbox_workspace_account_fk"
|
|
155
|
+
FOREIGN KEY ("workspace_id", "account_id")
|
|
156
|
+
REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
|
|
157
|
+
ALTER TABLE "session_system_update_outbox" ADD CONSTRAINT "system_update_outbox_source_session_fk"
|
|
158
|
+
FOREIGN KEY ("workspace_id", "source_session_id")
|
|
159
|
+
REFERENCES "sessions"("workspace_id", "id") ON DELETE CASCADE;
|
|
160
|
+
ALTER TABLE "session_system_update_outbox" ADD CONSTRAINT "system_update_outbox_target_session_fk"
|
|
161
|
+
FOREIGN KEY ("workspace_id", "target_session_id")
|
|
162
|
+
REFERENCES "sessions"("workspace_id", "id") ON DELETE CASCADE;
|
|
163
|
+
ALTER TABLE "session_pending_tool_calls" ADD CONSTRAINT "pending_tool_calls_workspace_account_fk"
|
|
164
|
+
FOREIGN KEY ("workspace_id", "account_id")
|
|
165
|
+
REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE;
|
|
166
|
+
ALTER TABLE "session_pending_tool_calls" ADD CONSTRAINT "pending_tool_calls_workspace_session_fk"
|
|
167
|
+
FOREIGN KEY ("workspace_id", "session_id")
|
|
168
|
+
REFERENCES "sessions"("workspace_id", "id") ON DELETE CASCADE;
|
|
169
|
+
ALTER TABLE "session_pending_tool_calls" ADD CONSTRAINT "pending_tool_calls_workspace_turn_fk"
|
|
170
|
+
FOREIGN KEY ("workspace_id", "turn_id")
|
|
171
|
+
REFERENCES "session_turns"("workspace_id", "id") ON DELETE CASCADE;
|
|
172
|
+
|
|
173
|
+
-- Preserve historical evidence while normalizing every event name to the new
|
|
174
|
+
-- vocabulary before new workers parse it.
|
|
175
|
+
UPDATE "session_events" SET "type" = CASE "type"
|
|
176
|
+
WHEN 'user.interrupt' THEN 'user.pause'
|
|
177
|
+
WHEN 'turn.preempted' THEN 'turn.recovery.requested'
|
|
178
|
+
WHEN 'turn.updated' THEN 'session.queue.history'
|
|
179
|
+
WHEN 'turn.queue_drained' THEN 'session.queue.history'
|
|
180
|
+
WHEN 'system.update.bundle' THEN 'system.update.pending'
|
|
181
|
+
WHEN 'system.update.bundle.updated' THEN 'system.update.pending'
|
|
182
|
+
WHEN 'session.control.stopped' THEN 'session.control.paused'
|
|
183
|
+
WHEN 'session.control.interrupt_requested' THEN 'session.control.paused'
|
|
184
|
+
WHEN 'workspace.inference.killed' THEN 'workspace.inference.paused'
|
|
185
|
+
WHEN 'session.queue.item.edited' THEN 'session.queue.history'
|
|
186
|
+
WHEN 'session.queue.item.promoted' THEN 'session.queue.history'
|
|
187
|
+
WHEN 'session.queue.reordered' THEN 'session.queue.history'
|
|
188
|
+
WHEN 'session.queue.item.cancelled' THEN 'session.queue.prompt.cancelled'
|
|
189
|
+
ELSE "type"
|
|
190
|
+
END
|
|
191
|
+
WHERE "type" IN (
|
|
192
|
+
'user.interrupt','turn.preempted','turn.updated','turn.queue_drained','system.update.bundle',
|
|
193
|
+
'system.update.bundle.updated','session.control.stopped',
|
|
194
|
+
'session.control.interrupt_requested','workspace.inference.killed',
|
|
195
|
+
'session.queue.item.edited','session.queue.item.promoted',
|
|
196
|
+
'session.queue.reordered','session.queue.item.cancelled'
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
UPDATE "session_goals"
|
|
200
|
+
SET "paused_reason" = 'user_pause'
|
|
201
|
+
WHERE "paused_reason" = 'user_interrupt';
|
|
202
|
+
|
|
203
|
+
-- A drained turn that started before and was put back into the old queue is
|
|
204
|
+
-- the same current inference in recovery, not a waiting prompt. The drain gate
|
|
205
|
+
-- guarantees no old attempt still owns it here.
|
|
206
|
+
UPDATE "session_turns" t
|
|
207
|
+
SET "status" = 'recovering',
|
|
208
|
+
"active_attempt_id" = NULL,
|
|
209
|
+
"updated_at" = now()
|
|
210
|
+
WHERE t."status" = 'running'
|
|
211
|
+
OR (
|
|
212
|
+
t."status" = 'queued'
|
|
213
|
+
AND EXISTS (
|
|
214
|
+
SELECT 1 FROM "session_events" e
|
|
215
|
+
WHERE e."workspace_id" = t."workspace_id"
|
|
216
|
+
AND e."session_id" = t."session_id"
|
|
217
|
+
AND e."turn_id" = t."id"
|
|
218
|
+
AND e."type" = 'turn.started'
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
-- Unstarted reusable-session machine work moves to the typed update plane.
|
|
223
|
+
INSERT INTO "session_system_updates" (
|
|
224
|
+
"account_id", "workspace_id", "session_id", "kind", "classification",
|
|
225
|
+
"source_id", "dedupe_key", "summary", "payload", "lineage"
|
|
226
|
+
)
|
|
227
|
+
SELECT
|
|
228
|
+
t."account_id", t."workspace_id", t."session_id",
|
|
229
|
+
CASE WHEN t."source" = 'scheduled_task' THEN 'scheduled_wake' ELSE 'runtime_notice' END,
|
|
230
|
+
'info', t."id"::text, 'migrated-turn:' || t."id"::text, t."prompt",
|
|
231
|
+
jsonb_build_object('migratedTurnId', t."id", 'source', t."source"),
|
|
232
|
+
jsonb_build_object('migratedTurnId', t."id")
|
|
233
|
+
FROM "session_turns" t
|
|
234
|
+
WHERE t."status" = 'queued' AND t."source" IN ('scheduled_task','system');
|
|
235
|
+
|
|
236
|
+
-- The delivered-turn FK above is INITIALLY DEFERRED because normal runtime
|
|
237
|
+
-- fan-in may preallocate an update and its delivered turn in one transaction.
|
|
238
|
+
-- This migration still has table-level DDL below (RLS, constraints, indexes).
|
|
239
|
+
-- PostgreSQL refuses ALTER TABLE while a table has pending deferred constraint
|
|
240
|
+
-- triggers, even when every inserted delivered_turn_id is NULL. Settle the
|
|
241
|
+
-- migration's own inserts now; this changes only this transaction's constraint
|
|
242
|
+
-- mode and leaves the declared runtime default deferred.
|
|
243
|
+
SET CONSTRAINTS ALL IMMEDIATE;
|
|
244
|
+
|
|
245
|
+
UPDATE "session_turns"
|
|
246
|
+
SET "status" = 'superseded',
|
|
247
|
+
"cancelled_by" = 'control-plane-cutover',
|
|
248
|
+
"cancel_reason" = 'migrated_to_internal_update',
|
|
249
|
+
"finished_at" = now(),
|
|
250
|
+
"updated_at" = now()
|
|
251
|
+
WHERE "status" = 'queued' AND "source" IN ('scheduled_task','system');
|
|
252
|
+
|
|
253
|
+
-- An unstarted goal row carries no truth that is not already in session_goals;
|
|
254
|
+
-- the generalized wake scan will continue the active goal after cutover.
|
|
255
|
+
UPDATE "session_turns"
|
|
256
|
+
SET "status" = 'superseded',
|
|
257
|
+
"cancelled_by" = 'control-plane-cutover',
|
|
258
|
+
"cancel_reason" = 'goal_continuation_is_not_queue_work',
|
|
259
|
+
"finished_at" = now(),
|
|
260
|
+
"updated_at" = now()
|
|
261
|
+
WHERE "status" = 'queued' AND "source" = 'goal';
|
|
262
|
+
|
|
263
|
+
DO $$
|
|
264
|
+
BEGIN
|
|
265
|
+
IF EXISTS (
|
|
266
|
+
SELECT 1 FROM "session_turns"
|
|
267
|
+
WHERE "status" = 'queued' AND "source" NOT IN ('user','api')
|
|
268
|
+
) THEN
|
|
269
|
+
RAISE EXCEPTION 'unclassified queued machine turn remains at control-plane cutover';
|
|
270
|
+
END IF;
|
|
271
|
+
|
|
272
|
+
IF EXISTS (
|
|
273
|
+
SELECT 1 FROM "session_turns"
|
|
274
|
+
WHERE "status" NOT IN (
|
|
275
|
+
'queued','running','requires_action','recovering','waiting_capacity',
|
|
276
|
+
'completed','failed','cancelled','superseded'
|
|
277
|
+
)
|
|
278
|
+
) THEN
|
|
279
|
+
RAISE EXCEPTION 'unknown session turn status at control-plane cutover';
|
|
280
|
+
END IF;
|
|
281
|
+
|
|
282
|
+
IF EXISTS (
|
|
283
|
+
SELECT 1
|
|
284
|
+
FROM "session_turns"
|
|
285
|
+
WHERE "status" IN ('running','requires_action','recovering','waiting_capacity')
|
|
286
|
+
GROUP BY "workspace_id", "session_id"
|
|
287
|
+
HAVING count(*) > 1
|
|
288
|
+
) THEN
|
|
289
|
+
RAISE EXCEPTION 'multiple current inferences found for one session at control-plane cutover';
|
|
290
|
+
END IF;
|
|
291
|
+
|
|
292
|
+
IF EXISTS (
|
|
293
|
+
SELECT 1 FROM "session_history_items"
|
|
294
|
+
WHERE "active" = true AND "item" ->> 'type' IN ('compaction','compaction_summary')
|
|
295
|
+
) THEN
|
|
296
|
+
RAISE EXCEPTION 'active opaque remote compaction item found at control-plane cutover';
|
|
297
|
+
END IF;
|
|
298
|
+
END $$;
|
|
299
|
+
|
|
300
|
+
UPDATE "sessions" s
|
|
301
|
+
SET "active_turn_id" = current_turn."id"
|
|
302
|
+
FROM (
|
|
303
|
+
SELECT "workspace_id", "session_id", max("id"::text)::uuid AS "id"
|
|
304
|
+
FROM "session_turns"
|
|
305
|
+
WHERE "status" IN ('running','requires_action','recovering','waiting_capacity')
|
|
306
|
+
GROUP BY "workspace_id", "session_id"
|
|
307
|
+
) current_turn
|
|
308
|
+
WHERE s."workspace_id" = current_turn."workspace_id"
|
|
309
|
+
AND s."id" = current_turn."session_id";
|
|
310
|
+
|
|
311
|
+
UPDATE "sessions" s
|
|
312
|
+
SET "active_turn_id" = NULL
|
|
313
|
+
WHERE NOT EXISTS (
|
|
314
|
+
SELECT 1 FROM "session_turns" t
|
|
315
|
+
WHERE t."workspace_id" = s."workspace_id"
|
|
316
|
+
AND t."session_id" = s."id"
|
|
317
|
+
AND t."status" IN ('running','requires_action','recovering','waiting_capacity')
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
UPDATE "sessions" s
|
|
321
|
+
SET "queue_head_position" = bounds."head",
|
|
322
|
+
"queue_tail_position" = bounds."tail"
|
|
323
|
+
FROM (
|
|
324
|
+
SELECT "workspace_id", "session_id",
|
|
325
|
+
coalesce(min("position"), 0) AS "head",
|
|
326
|
+
coalesce(max("position"), 0) AS "tail"
|
|
327
|
+
FROM "session_turns"
|
|
328
|
+
GROUP BY "workspace_id", "session_id"
|
|
329
|
+
) bounds
|
|
330
|
+
WHERE s."workspace_id" = bounds."workspace_id"
|
|
331
|
+
AND s."id" = bounds."session_id";
|
|
332
|
+
|
|
333
|
+
UPDATE "sessions" s
|
|
334
|
+
SET "status" = CASE
|
|
335
|
+
WHEN s."control_state" = 'paused' THEN 'paused'
|
|
336
|
+
WHEN t."status" = 'requires_action' THEN 'requires_action'
|
|
337
|
+
WHEN t."status" = 'recovering' THEN 'recovering'
|
|
338
|
+
WHEN t."status" = 'waiting_capacity' THEN 'waiting_capacity'
|
|
339
|
+
WHEN t."status" = 'running' THEN 'running'
|
|
340
|
+
WHEN EXISTS (
|
|
341
|
+
SELECT 1 FROM "session_turns" q
|
|
342
|
+
WHERE q."workspace_id" = s."workspace_id"
|
|
343
|
+
AND q."session_id" = s."id" AND q."status" = 'queued'
|
|
344
|
+
) THEN 'queued'
|
|
345
|
+
ELSE 'idle'
|
|
346
|
+
END
|
|
347
|
+
FROM "session_turns" t
|
|
348
|
+
WHERE t."workspace_id" = s."workspace_id"
|
|
349
|
+
AND t."id" = s."active_turn_id";
|
|
350
|
+
|
|
351
|
+
UPDATE "sessions" s
|
|
352
|
+
SET "status" = CASE
|
|
353
|
+
WHEN s."control_state" = 'paused' THEN 'paused'
|
|
354
|
+
WHEN EXISTS (
|
|
355
|
+
SELECT 1 FROM "session_turns" q
|
|
356
|
+
WHERE q."workspace_id" = s."workspace_id"
|
|
357
|
+
AND q."session_id" = s."id" AND q."status" = 'queued'
|
|
358
|
+
) THEN 'queued'
|
|
359
|
+
ELSE 'idle'
|
|
360
|
+
END
|
|
361
|
+
WHERE s."active_turn_id" IS NULL;
|
|
362
|
+
|
|
363
|
+
ALTER TABLE "workspaces" ADD CONSTRAINT "workspaces_inference_state_check"
|
|
364
|
+
CHECK ("inference_state" IN ('active','paused'));
|
|
365
|
+
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_control_state_check"
|
|
366
|
+
CHECK ("control_state" IN ('active','paused'));
|
|
367
|
+
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_pending_control_kind_check"
|
|
368
|
+
CHECK ("pending_control_kind" IS NULL OR "pending_control_kind" IN ('pause','steer'));
|
|
369
|
+
ALTER TABLE "session_turns" ADD CONSTRAINT "session_turns_status_check"
|
|
370
|
+
CHECK ("status" IN (
|
|
371
|
+
'queued','running','requires_action','recovering','waiting_capacity',
|
|
372
|
+
'completed','failed','cancelled','superseded'
|
|
373
|
+
));
|
|
374
|
+
ALTER TABLE "session_turns" ADD CONSTRAINT "session_turns_queue_purity_check"
|
|
375
|
+
CHECK ("status" <> 'queued' OR "source" IN ('user','api'));
|
|
376
|
+
ALTER TABLE "session_system_updates" ADD CONSTRAINT "system_updates_kind_check"
|
|
377
|
+
CHECK ("kind" IN ('child_session_update','scheduled_wake','lifecycle_event','runtime_notice'));
|
|
378
|
+
ALTER TABLE "session_system_updates" ADD CONSTRAINT "system_updates_classification_check"
|
|
379
|
+
CHECK ("classification" IN ('success','failure','action_required','info'));
|
|
380
|
+
ALTER TABLE "session_system_updates" ADD CONSTRAINT "system_updates_state_check"
|
|
381
|
+
CHECK ("state" IN ('pending','delivered','cancelled','failed'));
|
|
382
|
+
ALTER TABLE "runtime_control_operations" ADD CONSTRAINT "runtime_control_operations_scope_check"
|
|
383
|
+
CHECK ("scope" IN ('session','workspace'));
|
|
384
|
+
ALTER TABLE "session_system_update_outbox" ADD CONSTRAINT "system_update_outbox_status_check"
|
|
385
|
+
CHECK ("status" IN ('pending','delivered','failed'));
|
|
386
|
+
ALTER TABLE "session_events" ADD CONSTRAINT "session_events_turn_association_check"
|
|
387
|
+
CHECK ("turn_association" IS NULL OR "turn_association" IN ('current','late_rejected'));
|
|
388
|
+
|
|
389
|
+
CREATE UNIQUE INDEX "session_turns_one_current_inference_uq"
|
|
390
|
+
ON "session_turns" ("workspace_id", "session_id")
|
|
391
|
+
WHERE "status" IN ('running','requires_action','recovering','waiting_capacity');
|
|
392
|
+
CREATE INDEX "session_turns_prompt_queue_idx"
|
|
393
|
+
ON "session_turns" ("workspace_id", "session_id", "position")
|
|
394
|
+
WHERE "status" = 'queued';
|
|
395
|
+
CREATE UNIQUE INDEX "session_system_updates_dedupe_uq"
|
|
396
|
+
ON "session_system_updates" ("workspace_id", "session_id", "dedupe_key");
|
|
397
|
+
CREATE INDEX "session_system_updates_pending_idx"
|
|
398
|
+
ON "session_system_updates" ("workspace_id", "session_id", "state", "created_at");
|
|
399
|
+
CREATE UNIQUE INDEX "runtime_control_operations_client_uq"
|
|
400
|
+
ON "runtime_control_operations" ("workspace_id", "client_event_id");
|
|
401
|
+
CREATE INDEX "runtime_control_operations_target_idx"
|
|
402
|
+
ON "runtime_control_operations" ("workspace_id", "scope", "target_id", "created_at");
|
|
403
|
+
CREATE UNIQUE INDEX "session_system_update_outbox_dedupe_uq"
|
|
404
|
+
ON "session_system_update_outbox" ("workspace_id", "dedupe_key");
|
|
405
|
+
CREATE INDEX "session_system_update_outbox_pending_idx"
|
|
406
|
+
ON "session_system_update_outbox" ("status", "created_at");
|
|
407
|
+
CREATE UNIQUE INDEX "session_pending_tool_calls_turn_call_idx"
|
|
408
|
+
ON "session_pending_tool_calls" ("workspace_id", "turn_id", "call_id");
|
|
409
|
+
CREATE INDEX "session_pending_tool_calls_session_turn_idx"
|
|
410
|
+
ON "session_pending_tool_calls" ("workspace_id", "session_id", "turn_id");
|
|
411
|
+
CREATE UNIQUE INDEX "scheduled_task_runs_producer_key_uq"
|
|
412
|
+
ON "scheduled_task_runs" ("workspace_id", "producer_key")
|
|
413
|
+
WHERE "producer_key" IS NOT NULL;
|
|
414
|
+
|
|
415
|
+
CREATE OR REPLACE FUNCTION opengeni_private.enforce_session_inference_claim()
|
|
416
|
+
RETURNS trigger
|
|
417
|
+
LANGUAGE plpgsql
|
|
418
|
+
SECURITY DEFINER
|
|
419
|
+
SET search_path = pg_catalog
|
|
420
|
+
AS $$
|
|
421
|
+
BEGIN
|
|
422
|
+
IF NEW.status = 'running' AND OLD.status <> 'running'
|
|
423
|
+
AND coalesce(current_setting('opengeni.session_inference_claim', true), '') <> '1' THEN
|
|
424
|
+
RAISE EXCEPTION 'session inference must be claimed through the fenced claim transaction'
|
|
425
|
+
USING ERRCODE = '55000';
|
|
426
|
+
END IF;
|
|
427
|
+
RETURN NEW;
|
|
428
|
+
END $$;
|
|
429
|
+
CREATE TRIGGER session_turns_inference_claim_guard
|
|
430
|
+
BEFORE UPDATE OF status ON "session_turns" FOR EACH ROW
|
|
431
|
+
EXECUTE FUNCTION opengeni_private.enforce_session_inference_claim();
|
|
432
|
+
|
|
433
|
+
ALTER TABLE "session_system_updates" ENABLE ROW LEVEL SECURITY;
|
|
434
|
+
ALTER TABLE "session_system_updates" FORCE ROW LEVEL SECURITY;
|
|
435
|
+
ALTER TABLE "runtime_control_operations" ENABLE ROW LEVEL SECURITY;
|
|
436
|
+
ALTER TABLE "runtime_control_operations" FORCE ROW LEVEL SECURITY;
|
|
437
|
+
ALTER TABLE "session_system_update_outbox" ENABLE ROW LEVEL SECURITY;
|
|
438
|
+
ALTER TABLE "session_system_update_outbox" FORCE ROW LEVEL SECURITY;
|
|
439
|
+
ALTER TABLE "session_pending_tool_calls" ENABLE ROW LEVEL SECURITY;
|
|
440
|
+
ALTER TABLE "session_pending_tool_calls" FORCE ROW LEVEL SECURITY;
|
|
441
|
+
CREATE POLICY workspace_isolation ON "session_system_updates"
|
|
442
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
443
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
444
|
+
CREATE POLICY workspace_isolation ON "runtime_control_operations"
|
|
445
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
446
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
447
|
+
CREATE POLICY workspace_isolation ON "session_system_update_outbox"
|
|
448
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
449
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
450
|
+
CREATE POLICY workspace_isolation ON "session_pending_tool_calls"
|
|
451
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
452
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
453
|
+
|
|
454
|
+
-- Bounded cross-workspace recovery claim for the worker outbox reconciler.
|
|
455
|
+
CREATE OR REPLACE FUNCTION opengeni_private.claim_session_system_update_outbox(p_limit integer)
|
|
456
|
+
RETURNS TABLE (
|
|
457
|
+
id uuid, account_id uuid, workspace_id uuid, source_session_id uuid,
|
|
458
|
+
target_session_id uuid, dedupe_key text, kind text, classification text,
|
|
459
|
+
source_id text, summary text, payload jsonb, lineage jsonb
|
|
460
|
+
)
|
|
461
|
+
LANGUAGE plpgsql
|
|
462
|
+
SECURITY DEFINER
|
|
463
|
+
AS $$
|
|
464
|
+
BEGIN
|
|
465
|
+
RETURN QUERY
|
|
466
|
+
WITH claimed AS (
|
|
467
|
+
SELECT o.id FROM session_system_update_outbox o
|
|
468
|
+
WHERE o.status = 'pending'
|
|
469
|
+
ORDER BY o.created_at, o.id
|
|
470
|
+
FOR UPDATE SKIP LOCKED
|
|
471
|
+
LIMIT greatest(1, least(coalesce(p_limit, 100), 100))
|
|
472
|
+
)
|
|
473
|
+
UPDATE session_system_update_outbox o
|
|
474
|
+
SET attempts = o.attempts + 1, updated_at = now()
|
|
475
|
+
FROM claimed c WHERE o.id = c.id
|
|
476
|
+
RETURNING o.id, o.account_id, o.workspace_id, o.source_session_id,
|
|
477
|
+
o.target_session_id, o.dedupe_key, o.kind, o.classification,
|
|
478
|
+
o.source_id, o.summary, o.payload, o.lineage;
|
|
479
|
+
END $$;
|
|
480
|
+
REVOKE ALL ON FUNCTION opengeni_private.claim_session_system_update_outbox(integer) FROM PUBLIC;
|
|
481
|
+
|
|
482
|
+
-- Generalized post-cutover repair scan. It returns each runnable session once;
|
|
483
|
+
-- the workflow claim transaction chooses recovery, prompt, then update/goal.
|
|
484
|
+
CREATE OR REPLACE FUNCTION opengeni_private.list_claimable_sessions(p_limit integer)
|
|
485
|
+
RETURNS TABLE (
|
|
486
|
+
account_id uuid, workspace_id uuid, session_id uuid, temporal_workflow_id text
|
|
487
|
+
)
|
|
488
|
+
LANGUAGE sql
|
|
489
|
+
SECURITY DEFINER
|
|
490
|
+
AS $$
|
|
491
|
+
SELECT DISTINCT s.account_id, s.workspace_id, s.id,
|
|
492
|
+
coalesce(s.temporal_workflow_id, 'session-' || s.id::text)
|
|
493
|
+
FROM sessions s
|
|
494
|
+
JOIN workspaces w ON w.id = s.workspace_id
|
|
495
|
+
WHERE s.control_state = 'active'
|
|
496
|
+
AND (
|
|
497
|
+
w.inference_state = 'active'
|
|
498
|
+
OR s.workspace_run_exception_generation = w.inference_generation
|
|
499
|
+
)
|
|
500
|
+
AND s.pending_control_event_id IS NULL
|
|
501
|
+
AND (
|
|
502
|
+
EXISTS (
|
|
503
|
+
SELECT 1 FROM session_turns t
|
|
504
|
+
WHERE t.workspace_id = s.workspace_id AND t.session_id = s.id
|
|
505
|
+
AND t.status IN ('queued','recovering','waiting_capacity')
|
|
506
|
+
)
|
|
507
|
+
OR EXISTS (
|
|
508
|
+
SELECT 1 FROM session_system_updates u
|
|
509
|
+
WHERE u.workspace_id = s.workspace_id AND u.session_id = s.id
|
|
510
|
+
AND u.state = 'pending'
|
|
511
|
+
)
|
|
512
|
+
OR EXISTS (
|
|
513
|
+
SELECT 1 FROM session_goals g
|
|
514
|
+
WHERE g.workspace_id = s.workspace_id AND g.session_id = s.id
|
|
515
|
+
AND g.status = 'active'
|
|
516
|
+
)
|
|
517
|
+
)
|
|
518
|
+
ORDER BY s.id
|
|
519
|
+
LIMIT greatest(1, least(coalesce(p_limit, 1000), 10000));
|
|
520
|
+
$$;
|
|
521
|
+
REVOKE ALL ON FUNCTION opengeni_private.list_claimable_sessions(integer) FROM PUBLIC;
|
|
522
|
+
|
|
523
|
+
DO $$
|
|
524
|
+
DECLARE target_schema text := current_schema();
|
|
525
|
+
BEGIN
|
|
526
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
527
|
+
EXECUTE format(
|
|
528
|
+
'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO opengeni_app',
|
|
529
|
+
target_schema
|
|
530
|
+
);
|
|
531
|
+
GRANT EXECUTE ON FUNCTION opengeni_private.claim_session_system_update_outbox(integer)
|
|
532
|
+
TO opengeni_app;
|
|
533
|
+
GRANT EXECUTE ON FUNCTION opengeni_private.list_claimable_sessions(integer)
|
|
534
|
+
TO opengeni_app;
|
|
535
|
+
END IF;
|
|
536
|
+
END $$;
|