@opengeni/db 0.13.4 → 0.14.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.
@@ -0,0 +1,359 @@
1
+ -- deployment-mode: maintenance
2
+ -- Machine inputs become append-only model memory. This one-way cutover also
3
+ -- reconstructs every previously delivered batch and removes the old deferred
4
+ -- retry state; old workers must be drained before this migration runs.
5
+
6
+ ALTER TABLE "session_system_updates"
7
+ ADD COLUMN "delivered_history_item_id" uuid;
8
+
9
+ UPDATE "session_system_updates"
10
+ SET "state" = 'pending'
11
+ WHERE "state" = 'deferred';
12
+
13
+ WITH ranked_pending_steers AS (
14
+ SELECT
15
+ id,
16
+ row_number() OVER (
17
+ PARTITION BY workspace_id, session_id
18
+ ORDER BY created_at DESC, id DESC
19
+ ) AS rank
20
+ FROM "session_system_updates"
21
+ WHERE kind = 'agent_steer_instruction'
22
+ AND state = 'pending'
23
+ )
24
+ UPDATE "session_system_updates" updates
25
+ SET state = 'superseded'
26
+ FROM ranked_pending_steers ranked
27
+ WHERE updates.id = ranked.id
28
+ AND ranked.rank > 1;
29
+
30
+ ALTER TABLE "session_system_updates"
31
+ DROP CONSTRAINT "system_updates_state_check";
32
+
33
+ ALTER TABLE "session_system_updates"
34
+ ADD CONSTRAINT "system_updates_state_check"
35
+ CHECK ("state" IN ('pending', 'delivered', 'cancelled', 'superseded', 'failed'));
36
+
37
+ WITH delivered_batches AS (
38
+ SELECT
39
+ updates.account_id,
40
+ updates.workspace_id,
41
+ updates.session_id,
42
+ updates.delivered_turn_id AS turn_id,
43
+ gen_random_uuid() AS history_item_id,
44
+ min(updates.delivered_at) AS delivered_at,
45
+ jsonb_build_object(
46
+ 'type', 'message',
47
+ 'role', 'system',
48
+ 'content',
49
+ '[OpenGeni internal updates]' || E'\n' ||
50
+ 'These platform updates were delivered together for this inference. They are not human prompts.' || E'\n' ||
51
+ jsonb_build_object(
52
+ 'updates',
53
+ jsonb_agg(
54
+ jsonb_build_object(
55
+ 'id', updates.id,
56
+ 'kind', updates.kind,
57
+ 'classification', updates.classification,
58
+ 'sourceId', updates.source_id,
59
+ 'summary', updates.summary,
60
+ 'payload', updates.payload,
61
+ 'lineage', updates.lineage
62
+ )
63
+ ORDER BY
64
+ CASE WHEN updates.kind = 'agent_steer_instruction' THEN 1 ELSE 0 END,
65
+ updates.created_at,
66
+ updates.id
67
+ )
68
+ )::text
69
+ ) AS item
70
+ FROM "session_system_updates" updates
71
+ WHERE updates.state = 'delivered'
72
+ AND updates.delivered_turn_id IS NOT NULL
73
+ AND updates.delivered_history_item_id IS NULL
74
+ GROUP BY
75
+ updates.account_id,
76
+ updates.workspace_id,
77
+ updates.session_id,
78
+ updates.delivered_turn_id
79
+ ),
80
+ anchored AS (
81
+ SELECT
82
+ batches.*,
83
+ turns.position AS turn_position,
84
+ turns.created_at AS turn_created_at,
85
+ turns.id AS turn_order_id,
86
+ NOT EXISTS (
87
+ SELECT 1
88
+ FROM "session_events" reset_event
89
+ WHERE reset_event.workspace_id = batches.workspace_id
90
+ AND reset_event.session_id = batches.session_id
91
+ AND reset_event.type IN ('session.context.compacted', 'session.context.cleared')
92
+ AND reset_event.occurred_at >= coalesce(batches.delivered_at, turns.created_at)
93
+ ) AS preserve_active,
94
+ (
95
+ SELECT max(history.position)
96
+ FROM "session_history_items" history
97
+ WHERE history.workspace_id = batches.workspace_id
98
+ AND history.session_id = batches.session_id
99
+ AND history.turn_id = batches.turn_id
100
+ AND history.active
101
+ AND history.item ->> 'type' = 'message'
102
+ AND history.item ->> 'role' = 'user'
103
+ ) AS user_position,
104
+ (
105
+ SELECT min(history.position)
106
+ FROM "session_history_items" history
107
+ WHERE history.workspace_id = batches.workspace_id
108
+ AND history.session_id = batches.session_id
109
+ AND history.turn_id = batches.turn_id
110
+ AND history.active
111
+ ) AS turn_first_position,
112
+ (
113
+ SELECT coalesce(max(history.position), -1)
114
+ FROM "session_history_items" history
115
+ WHERE history.workspace_id = batches.workspace_id
116
+ AND history.session_id = batches.session_id
117
+ AND history.active
118
+ ) AS active_tail_position,
119
+ (
120
+ SELECT coalesce(max(history.position), -1)
121
+ FROM "session_history_items" history
122
+ WHERE history.workspace_id = batches.workspace_id
123
+ AND history.session_id = batches.session_id
124
+ ) AS audit_tail_position,
125
+ (
126
+ SELECT max(history.position)
127
+ FROM "session_history_items" history
128
+ JOIN "session_turns" history_turn
129
+ ON history_turn.workspace_id = history.workspace_id
130
+ AND history_turn.id = history.turn_id
131
+ WHERE history.workspace_id = batches.workspace_id
132
+ AND history.session_id = batches.session_id
133
+ AND history.active
134
+ AND (history_turn.position, history_turn.created_at, history_turn.id)
135
+ < (turns.position, turns.created_at, turns.id)
136
+ ) AS previous_active_position,
137
+ (
138
+ SELECT min(history.position)
139
+ FROM "session_history_items" history
140
+ JOIN "session_turns" history_turn
141
+ ON history_turn.workspace_id = history.workspace_id
142
+ AND history_turn.id = history.turn_id
143
+ WHERE history.workspace_id = batches.workspace_id
144
+ AND history.session_id = batches.session_id
145
+ AND history.active
146
+ AND (history_turn.position, history_turn.created_at, history_turn.id)
147
+ > (turns.position, turns.created_at, turns.id)
148
+ ) AS next_active_position,
149
+ row_number() OVER (
150
+ PARTITION BY batches.workspace_id, batches.session_id
151
+ ORDER BY turns.position, turns.created_at, turns.id
152
+ ) AS turn_ordinal
153
+ FROM delivered_batches batches
154
+ JOIN "session_turns" turns
155
+ ON turns.workspace_id = batches.workspace_id
156
+ AND turns.id = batches.turn_id
157
+ ),
158
+ gap_ranked AS (
159
+ SELECT
160
+ anchored.*,
161
+ row_number() OVER (
162
+ PARTITION BY
163
+ workspace_id,
164
+ session_id,
165
+ preserve_active,
166
+ user_position,
167
+ turn_first_position,
168
+ previous_active_position,
169
+ next_active_position
170
+ ORDER BY turn_position, turn_created_at, turn_order_id
171
+ ) AS gap_ordinal,
172
+ count(*) OVER (
173
+ PARTITION BY
174
+ workspace_id,
175
+ session_id,
176
+ preserve_active,
177
+ user_position,
178
+ turn_first_position,
179
+ previous_active_position,
180
+ next_active_position
181
+ ) AS gap_count
182
+ FROM anchored
183
+ ),
184
+ positioned AS (
185
+ SELECT
186
+ gap_ranked.*,
187
+ CASE
188
+ WHEN NOT preserve_active THEN audit_tail_position + turn_ordinal
189
+ WHEN user_position IS NOT NULL THEN
190
+ user_position
191
+ + (
192
+ coalesce(
193
+ (
194
+ SELECT min(history.position)
195
+ FROM "session_history_items" history
196
+ WHERE history.workspace_id = gap_ranked.workspace_id
197
+ AND history.session_id = gap_ranked.session_id
198
+ AND history.position > user_position
199
+ ),
200
+ user_position + 1
201
+ )
202
+ - user_position
203
+ ) * gap_ordinal / (gap_count + 1)
204
+ WHEN turn_first_position IS NOT NULL THEN
205
+ coalesce(
206
+ (
207
+ SELECT max(history.position)
208
+ FROM "session_history_items" history
209
+ WHERE history.workspace_id = gap_ranked.workspace_id
210
+ AND history.session_id = gap_ranked.session_id
211
+ AND history.position < turn_first_position
212
+ ),
213
+ turn_first_position - 2
214
+ )
215
+ + (
216
+ turn_first_position
217
+ - coalesce(
218
+ (
219
+ SELECT max(history.position)
220
+ FROM "session_history_items" history
221
+ WHERE history.workspace_id = gap_ranked.workspace_id
222
+ AND history.session_id = gap_ranked.session_id
223
+ AND history.position < turn_first_position
224
+ ),
225
+ turn_first_position - 2
226
+ )
227
+ ) * gap_ordinal / (gap_count + 1)
228
+ WHEN previous_active_position IS NOT NULL AND next_active_position IS NOT NULL THEN
229
+ previous_active_position
230
+ + (next_active_position - previous_active_position) * gap_ordinal / (gap_count + 1)
231
+ WHEN previous_active_position IS NOT NULL THEN
232
+ previous_active_position + gap_ordinal
233
+ WHEN next_active_position IS NOT NULL THEN
234
+ next_active_position - (gap_count - gap_ordinal + 1)
235
+ ELSE active_tail_position + gap_ordinal
236
+ END AS history_position
237
+ FROM gap_ranked
238
+ ),
239
+ existing_gaps AS (
240
+ SELECT
241
+ positioned.*,
242
+ coalesce(
243
+ (
244
+ SELECT max(history.position)
245
+ FROM "session_history_items" history
246
+ WHERE history.workspace_id = positioned.workspace_id
247
+ AND history.session_id = positioned.session_id
248
+ AND history.position < positioned.history_position
249
+ ),
250
+ positioned.history_position - 1
251
+ ) AS existing_gap_left,
252
+ coalesce(
253
+ (
254
+ SELECT min(history.position)
255
+ FROM "session_history_items" history
256
+ WHERE history.workspace_id = positioned.workspace_id
257
+ AND history.session_id = positioned.session_id
258
+ AND history.position > positioned.history_position
259
+ ),
260
+ positioned.history_position + 1
261
+ ) AS existing_gap_right
262
+ FROM positioned
263
+ ),
264
+ final_positioned AS (
265
+ SELECT
266
+ existing_gaps.*,
267
+ existing_gap_left
268
+ + (existing_gap_right - existing_gap_left)
269
+ * row_number() OVER (
270
+ PARTITION BY
271
+ workspace_id,
272
+ session_id,
273
+ existing_gap_left,
274
+ existing_gap_right
275
+ ORDER BY history_position, turn_position, turn_created_at, turn_order_id
276
+ )
277
+ / (
278
+ count(*) OVER (
279
+ PARTITION BY
280
+ workspace_id,
281
+ session_id,
282
+ existing_gap_left,
283
+ existing_gap_right
284
+ ) + 1
285
+ ) AS final_history_position
286
+ FROM existing_gaps
287
+ ),
288
+ inserted AS (
289
+ INSERT INTO "session_history_items" (
290
+ "id",
291
+ "account_id",
292
+ "workspace_id",
293
+ "session_id",
294
+ "turn_id",
295
+ "position",
296
+ "item",
297
+ "active",
298
+ "producer_codex_credential_id",
299
+ "created_at"
300
+ )
301
+ SELECT
302
+ history_item_id,
303
+ account_id,
304
+ workspace_id,
305
+ session_id,
306
+ turn_id,
307
+ final_history_position,
308
+ item,
309
+ preserve_active,
310
+ NULL,
311
+ coalesce(delivered_at, now())
312
+ FROM final_positioned
313
+ RETURNING id, workspace_id, session_id, turn_id
314
+ )
315
+ UPDATE "session_system_updates" updates
316
+ SET "delivered_history_item_id" = inserted.id
317
+ FROM inserted
318
+ WHERE updates.workspace_id = inserted.workspace_id
319
+ AND updates.session_id = inserted.session_id
320
+ AND updates.delivered_turn_id = inserted.turn_id
321
+ AND updates.state = 'delivered'
322
+ AND updates.delivered_history_item_id IS NULL;
323
+
324
+ DO $$
325
+ BEGIN
326
+ IF EXISTS (
327
+ SELECT 1
328
+ FROM "session_system_updates"
329
+ WHERE state = 'delivered'
330
+ AND delivered_history_item_id IS NULL
331
+ ) THEN
332
+ RAISE EXCEPTION
333
+ 'cannot prove model-memory position for one or more delivered machine inputs';
334
+ END IF;
335
+ END
336
+ $$;
337
+
338
+ ALTER TABLE "session_system_updates"
339
+ ADD CONSTRAINT "session_system_updates_history_item_fk"
340
+ FOREIGN KEY ("delivered_history_item_id")
341
+ REFERENCES "session_history_items"("id")
342
+ ON DELETE RESTRICT
343
+ DEFERRABLE INITIALLY DEFERRED;
344
+
345
+ ALTER TABLE "session_system_updates"
346
+ ADD CONSTRAINT "session_system_updates_delivery_history_check"
347
+ CHECK (
348
+ ("state" = 'delivered' AND "delivered_history_item_id" IS NOT NULL)
349
+ OR
350
+ ("state" <> 'delivered' AND "delivered_history_item_id" IS NULL)
351
+ );
352
+
353
+ CREATE INDEX "session_system_updates_history_item_idx"
354
+ ON "session_system_updates" ("workspace_id", "delivered_history_item_id")
355
+ WHERE "delivered_history_item_id" IS NOT NULL;
356
+
357
+ CREATE UNIQUE INDEX "session_system_updates_one_pending_steer_idx"
358
+ ON "session_system_updates" ("workspace_id", "session_id")
359
+ WHERE "kind" = 'agent_steer_instruction' AND "state" = 'pending';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "0.13.4",
3
+ "version": "0.14.0",
4
4
  "description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -51,8 +51,8 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "@opengeni/codex": "^0.2.7",
54
- "@opengeni/config": "^0.7.10",
55
- "@opengeni/contracts": "^0.21.0",
54
+ "@opengeni/config": "^0.7.11",
55
+ "@opengeni/contracts": "^0.22.0",
56
56
  "@opengeni/network": "^0.1.1",
57
57
  "drizzle-orm": "^0.45.2",
58
58
  "postgres": "^3.4.7"