@opengeni/db 0.10.7 → 0.12.1

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 (35) hide show
  1. package/dist/{chunk-P6PKXY5W.js → chunk-VUKRIBO5.js} +485 -12
  2. package/dist/chunk-VUKRIBO5.js.map +1 -0
  3. package/dist/{chunk-KW526IJA.js → chunk-Y5WZZVQK.js} +80 -4
  4. package/dist/chunk-Y5WZZVQK.js.map +1 -0
  5. package/dist/index.d.ts +3 -2
  6. package/dist/index.js +5114 -2085
  7. package/dist/index.js.map +1 -1
  8. package/dist/migrate.d.ts +6 -3
  9. package/dist/migrate.js +1 -1
  10. package/dist/provision-roles.d.ts +720 -63
  11. package/dist/{schema-CqkzrBRS.d.ts → schema-BejThLcd.d.ts} +2965 -1194
  12. package/dist/schema.d.ts +1 -1
  13. package/dist/schema.js +17 -1
  14. package/drizzle/0109_nested_agent_depth_expand.sql +42 -0
  15. package/drizzle/0110_nested_agent_depth_boundary.sql +480 -0
  16. package/drizzle/0111_nested_agent_depth_backfill.sql +49 -0
  17. package/drizzle/0112_nested_agent_depth_contract.sql +38 -0
  18. package/drizzle/0113_nested_agent_depth_validate.sql +13 -0
  19. package/drizzle/0114_nested_agent_depth_contract.sql +49 -0
  20. package/drizzle/0115_nested_agent_depth_validate.sql +11 -0
  21. package/drizzle/0116_nested_agent_depth_index.sql +4 -0
  22. package/drizzle/0117_sandbox_recovery_generations.sql +699 -0
  23. package/drizzle/0118_new_session_drafts.sql +59 -0
  24. package/drizzle/0119_pending_tool_output_policy.sql +5 -0
  25. package/drizzle/0120_durable_goal_wake.sql +360 -0
  26. package/drizzle/0121_goal_update_idempotency.sql +11 -0
  27. package/package.json +3 -3
  28. package/src/index.ts +5961 -1240
  29. package/src/migrate.ts +131 -2
  30. package/src/new-session-drafts.ts +144 -0
  31. package/src/schema.ts +519 -15
  32. package/src/session-control.ts +42 -18
  33. package/src/session-tool-call-settlement.ts +6 -1
  34. package/dist/chunk-KW526IJA.js.map +0 -1
  35. package/dist/chunk-P6PKXY5W.js.map +0 -1
@@ -0,0 +1,59 @@
1
+ -- deployment-mode: rolling
2
+ -- Actor-private, server-authoritative composer state before a session exists.
3
+ -- This is intentionally separate from composer_drafts, whose non-null session
4
+ -- foreign key is a load-bearing invariant for established-session queue edits.
5
+
6
+ SET lock_timeout = '5s';
7
+ SET statement_timeout = '10min';
8
+
9
+ CREATE TABLE "new_session_drafts" (
10
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
11
+ "account_id" uuid NOT NULL,
12
+ "workspace_id" uuid NOT NULL,
13
+ "subject_id" text NOT NULL,
14
+ "revision" bigint DEFAULT 1 NOT NULL,
15
+ "text" text DEFAULT '' NOT NULL,
16
+ "resources" jsonb DEFAULT '[]'::jsonb NOT NULL,
17
+ "tools" jsonb DEFAULT '[]'::jsonb NOT NULL,
18
+ "model" text NOT NULL,
19
+ "reasoning_effort" text NOT NULL,
20
+ "session_options" jsonb DEFAULT '{}'::jsonb NOT NULL,
21
+ "created_at" timestamptz DEFAULT now() NOT NULL,
22
+ "updated_at" timestamptz DEFAULT now() NOT NULL,
23
+ CONSTRAINT "new_session_drafts_workspace_account_fk"
24
+ FOREIGN KEY ("workspace_id", "account_id")
25
+ REFERENCES "workspaces" ("id", "account_id") ON DELETE CASCADE,
26
+ CONSTRAINT "new_session_drafts_subject_check"
27
+ CHECK (length(btrim("subject_id")) > 0),
28
+ CONSTRAINT "new_session_drafts_revision_check" CHECK ("revision" >= 1)
29
+ );
30
+
31
+ CREATE UNIQUE INDEX "new_session_drafts_subject_workspace_uq"
32
+ ON "new_session_drafts" ("workspace_id", "subject_id");
33
+
34
+ ALTER TABLE "new_session_drafts" ENABLE ROW LEVEL SECURITY;
35
+ ALTER TABLE "new_session_drafts" FORCE ROW LEVEL SECURITY;
36
+
37
+ CREATE POLICY workspace_isolation ON "new_session_drafts"
38
+ USING (
39
+ opengeni_private.workspace_rls_visible(account_id, workspace_id)
40
+ AND subject_id = opengeni_private.current_subject_id()
41
+ )
42
+ WITH CHECK (
43
+ opengeni_private.workspace_rls_visible(account_id, workspace_id)
44
+ AND subject_id = opengeni_private.current_subject_id()
45
+ );
46
+
47
+ DO $grants$
48
+ DECLARE target_schema text := current_schema();
49
+ BEGIN
50
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
51
+ EXECUTE format(
52
+ 'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.new_session_drafts TO opengeni_app',
53
+ target_schema
54
+ );
55
+ END IF;
56
+ END $grants$;
57
+
58
+ RESET statement_timeout;
59
+ RESET lock_timeout;
@@ -0,0 +1,5 @@
1
+ -- deployment-mode: rolling
2
+ -- Preserve the resolved exact-model tool-output policy with raw pending receipts
3
+ -- so any later recovery stores the same canonical model-facing bytes.
4
+ ALTER TABLE "session_pending_tool_calls"
5
+ ADD COLUMN "model_tool_output_truncation_tokens" integer;
@@ -0,0 +1,360 @@
1
+ -- deployment-mode: rolling
2
+ -- OPE-59: a goal-owned, monotonic continuation obligation. PostgreSQL is the
3
+ -- authority; Temporal receives only repairable workflow nudges.
4
+ --
5
+ -- Migrations run as the table owner, but FORCE ROW LEVEL SECURITY still makes
6
+ -- that owner subject to workspace policies. This cross-workspace repair has no
7
+ -- tenant GUC and must therefore relax only the owner-visible posture for the
8
+ -- duration of this file. The migration runner executes the file in one
9
+ -- implicit transaction, so a failure rolls back both the data repair and the
10
+ -- posture changes; the final FORCE statements restore the shipped posture on
11
+ -- success. The application role remains non-bypass and policy-bound.
12
+ ALTER TABLE "session_goals" NO FORCE ROW LEVEL SECURITY;
13
+ ALTER TABLE "session_system_updates" NO FORCE ROW LEVEL SECURITY;
14
+ ALTER TABLE "session_workflow_wake_outbox" NO FORCE ROW LEVEL SECURITY;
15
+ ALTER TABLE "sessions" NO FORCE ROW LEVEL SECURITY;
16
+ ALTER TABLE "workspace_inference_controls" NO FORCE ROW LEVEL SECURITY;
17
+ ALTER TABLE "session_turns" NO FORCE ROW LEVEL SECURITY;
18
+ ALTER TABLE "codex_capacity_waiters" NO FORCE ROW LEVEL SECURITY;
19
+
20
+ ALTER TABLE "session_goals"
21
+ ADD COLUMN IF NOT EXISTS "continuation_wake_revision" bigint NOT NULL DEFAULT 0,
22
+ ADD COLUMN IF NOT EXISTS "continuation_observed_revision" bigint NOT NULL DEFAULT 0;
23
+
24
+ DO $$
25
+ BEGIN
26
+ IF NOT EXISTS (
27
+ SELECT 1
28
+ FROM pg_constraint
29
+ WHERE conname = 'session_goals_continuation_revision_check'
30
+ AND conrelid = 'session_goals'::regclass
31
+ ) THEN
32
+ ALTER TABLE "session_goals"
33
+ ADD CONSTRAINT "session_goals_continuation_revision_check"
34
+ CHECK (
35
+ "continuation_wake_revision" >= 0
36
+ AND "continuation_observed_revision" >= 0
37
+ AND "continuation_observed_revision" <= "continuation_wake_revision"
38
+ AND "continuation_wake_revision" <= 9007199254740991
39
+ AND "continuation_observed_revision" <= 9007199254740991
40
+ );
41
+ END IF;
42
+ END $$;
43
+
44
+ -- Drizzle and every public workflow/client contract represent revisions as
45
+ -- JavaScript numbers. Reject manual corruption or impossible exhaustion in
46
+ -- PostgreSQL before a bigint can be rounded into another producer's revision.
47
+ DO $$
48
+ BEGIN
49
+ IF NOT EXISTS (
50
+ SELECT 1
51
+ FROM pg_constraint
52
+ WHERE conname = 'session_workflow_wake_outbox_revision_safe_check'
53
+ AND conrelid = 'session_workflow_wake_outbox'::regclass
54
+ ) THEN
55
+ ALTER TABLE "session_workflow_wake_outbox"
56
+ ADD CONSTRAINT "session_workflow_wake_outbox_revision_safe_check"
57
+ CHECK (
58
+ "wake_revision" <= 9007199254740991
59
+ AND "delivered_revision" <= 9007199254740991
60
+ );
61
+ END IF;
62
+ END $$;
63
+
64
+ -- Legacy evaluation and update insertion were separate commits. A lost
65
+ -- response could therefore leave more than one current-version continuation,
66
+ -- and old generic failure handling could leave the only one deferred (which
67
+ -- is intentionally not independently runnable). Preserve the earliest
68
+ -- admitted update, normalize it to pending, and cancel later duplicates before
69
+ -- assigning the baseline obligation.
70
+ WITH ranked AS (
71
+ SELECT update.id,
72
+ row_number() OVER (
73
+ PARTITION BY update.workspace_id, update.session_id
74
+ ORDER BY update.created_at, update.id
75
+ ) AS ordinal
76
+ FROM "session_system_updates" AS update
77
+ JOIN "session_goals" AS goal
78
+ ON goal.workspace_id = update.workspace_id
79
+ AND goal.session_id = update.session_id
80
+ AND update.payload ->> 'goalId' = goal.id::text
81
+ AND jsonb_typeof(update.payload -> 'goalVersion') = 'number'
82
+ AND update.payload ->> 'goalVersion' ~ '^[1-9][0-9]*$'
83
+ AND update.payload ->> 'goalVersion' = goal.version::text
84
+ WHERE goal.status = 'active'
85
+ AND update.kind = 'goal_continuation'
86
+ AND update.state IN ('pending', 'deferred')
87
+ )
88
+ UPDATE "session_system_updates" AS update
89
+ SET state = CASE WHEN ranked.ordinal = 1 THEN 'pending' ELSE 'cancelled' END,
90
+ delivered_turn_id = NULL,
91
+ delivered_at = NULL
92
+ FROM ranked
93
+ WHERE update.id = ranked.id;
94
+
95
+ -- A surviving current-version pending continuation is already-materialized
96
+ -- work. Mark one synthetic baseline revision observed so deployment cannot
97
+ -- manufacture another continuation beside it. This baseline is valid even
98
+ -- while admission is paused: Resume will rediscover the pending update and
99
+ -- register its own authoritative wake. Only effectively-active targets receive
100
+ -- a rollout wake here. The fresh wake is required even when an older signal
101
+ -- revision was acknowledged: its workflow may have died after signal delivery
102
+ -- but before claiming the update. A delivered update is not itself
103
+ -- outstanding: its owning nonterminal turn is protected by the repair query
104
+ -- below, while an idle goal whose delivered turn already settled still needs a
105
+ -- new obligation. Likewise, a stale-version update cannot satisfy the current
106
+ -- goal.
107
+ --
108
+ -- Keep this admission projection identical to projectEffectiveControl:
109
+ -- descendant overrides defeat only more-distant session pauses, any ancestry
110
+ -- override may defeat a workspace pause, and incomplete/cyclic/over-limit
111
+ -- ancestry fails closed. The recursive term admits a root at depth 10,000 but
112
+ -- stops before traversing past it, matching SESSION_ANCESTRY_LIMIT.
113
+ WITH RECURSIVE materialized AS (
114
+ SELECT goal.id, goal.account_id, goal.workspace_id, goal.session_id,
115
+ COALESCE(session.temporal_workflow_id, 'session-' || session.id::text) AS workflow_id
116
+ FROM "session_goals" AS goal
117
+ JOIN "sessions" AS session
118
+ ON session.workspace_id = goal.workspace_id AND session.id = goal.session_id
119
+ WHERE goal.status = 'active'
120
+ AND goal.continuation_wake_revision = 0
121
+ AND goal.continuation_observed_revision = 0
122
+ AND EXISTS (
123
+ SELECT 1
124
+ FROM "session_system_updates" AS update
125
+ WHERE update.workspace_id = goal.workspace_id
126
+ AND update.session_id = goal.session_id
127
+ AND update.kind = 'goal_continuation'
128
+ AND update.state = 'pending'
129
+ AND update.payload ->> 'goalId' = goal.id::text
130
+ AND jsonb_typeof(update.payload -> 'goalVersion') = 'number'
131
+ AND update.payload ->> 'goalVersion' ~ '^[1-9][0-9]*$'
132
+ AND update.payload ->> 'goalVersion' = goal.version::text
133
+ )
134
+ ), ancestry AS (
135
+ SELECT materialized.id AS goal_id, session.workspace_id,
136
+ session.id AS session_id, session.parent_session_id,
137
+ session.direct_control_state, session.direct_pause_revision,
138
+ session.subtree_run_override_revision,
139
+ 0::integer AS depth, ARRAY[session.id]::uuid[] AS path, false AS cycle
140
+ FROM materialized
141
+ JOIN "sessions" AS session
142
+ ON session.workspace_id = materialized.workspace_id
143
+ AND session.id = materialized.session_id
144
+ UNION ALL
145
+ SELECT child.goal_id, parent.workspace_id,
146
+ parent.id, parent.parent_session_id,
147
+ parent.direct_control_state, parent.direct_pause_revision,
148
+ parent.subtree_run_override_revision,
149
+ child.depth + 1, child.path || parent.id,
150
+ parent.id = ANY(child.path)
151
+ FROM ancestry AS child
152
+ JOIN "sessions" AS parent
153
+ ON parent.workspace_id = child.workspace_id
154
+ AND parent.id = child.parent_session_id
155
+ WHERE child.parent_session_id IS NOT NULL
156
+ AND NOT child.cycle
157
+ AND child.depth < 10000
158
+ ), admitted AS (
159
+ SELECT materialized.id
160
+ FROM materialized
161
+ JOIN "workspace_inference_controls" AS control
162
+ ON control.workspace_id = materialized.workspace_id
163
+ WHERE EXISTS (
164
+ SELECT 1
165
+ FROM ancestry AS root
166
+ WHERE root.goal_id = materialized.id
167
+ AND root.parent_session_id IS NULL
168
+ AND NOT root.cycle
169
+ )
170
+ AND NOT EXISTS (
171
+ SELECT 1
172
+ FROM ancestry AS invalid
173
+ WHERE invalid.goal_id = materialized.id AND invalid.cycle
174
+ )
175
+ AND NOT EXISTS (
176
+ SELECT 1
177
+ FROM ancestry AS pause
178
+ WHERE pause.goal_id = materialized.id
179
+ AND pause.direct_control_state = 'paused'
180
+ AND NOT EXISTS (
181
+ SELECT 1
182
+ FROM ancestry AS override
183
+ WHERE override.goal_id = materialized.id
184
+ AND override.depth < pause.depth
185
+ AND override.subtree_run_override_revision > pause.direct_pause_revision
186
+ )
187
+ )
188
+ AND (
189
+ control.workspace_state = 'active'
190
+ OR (
191
+ control.workspace_state = 'paused'
192
+ AND EXISTS (
193
+ SELECT 1
194
+ FROM ancestry AS override
195
+ WHERE override.goal_id = materialized.id
196
+ AND override.subtree_run_override_revision > control.workspace_pause_revision
197
+ )
198
+ )
199
+ )
200
+ ), baselined AS (
201
+ UPDATE "session_goals" AS goal
202
+ SET "continuation_wake_revision" = 1,
203
+ "continuation_observed_revision" = 1,
204
+ "updated_at" = now()
205
+ FROM materialized
206
+ WHERE goal.id = materialized.id
207
+ RETURNING materialized.account_id, materialized.workspace_id,
208
+ materialized.session_id, materialized.workflow_id,
209
+ EXISTS (SELECT 1 FROM admitted WHERE admitted.id = materialized.id) AS admitted
210
+ )
211
+ INSERT INTO "session_workflow_wake_outbox" (
212
+ "session_id", "account_id", "workspace_id", "temporal_workflow_id",
213
+ "wake_revision", "delivered_revision", "reason", "attempts",
214
+ "next_attempt_at", "created_at", "updated_at"
215
+ )
216
+ SELECT session_id, account_id, workspace_id, workflow_id,
217
+ 1, 0, 'goal_materialized_backfill', 0, now(), now(), now()
218
+ FROM baselined
219
+ WHERE admitted
220
+ ON CONFLICT ("session_id") DO UPDATE SET
221
+ "temporal_workflow_id" = EXCLUDED."temporal_workflow_id",
222
+ "wake_revision" = "session_workflow_wake_outbox"."wake_revision" + 1,
223
+ "reason" = EXCLUDED."reason",
224
+ "attempts" = 0,
225
+ "next_attempt_at" = LEAST("session_workflow_wake_outbox"."next_attempt_at", EXCLUDED."next_attempt_at"),
226
+ "last_error" = NULL,
227
+ "updated_at" = now();
228
+
229
+ -- An active, admitted-idle goal with neither work nor a capacity waiter is the
230
+ -- legacy catch-and-idle hole. Arm exactly one revision. Human/API queue rows,
231
+ -- live/approval turns, capacity waiters, and paused or malformed ancestry
232
+ -- remain authoritative and will arm or resume through their own boundary.
233
+ WITH RECURSIVE candidates AS (
234
+ SELECT goal.id, goal.account_id, goal.workspace_id, goal.session_id,
235
+ COALESCE(session.temporal_workflow_id, 'session-' || session.id::text) AS workflow_id
236
+ FROM "session_goals" AS goal
237
+ JOIN "sessions" AS session
238
+ ON session.workspace_id = goal.workspace_id AND session.id = goal.session_id
239
+ WHERE goal.status = 'active'
240
+ -- Only untouched legacy rows are deployment repair candidates. The
241
+ -- baseline update above deliberately changes already-materialized goals to
242
+ -- 1/1; accepting arbitrary equal revisions here would immediately re-arm
243
+ -- those same goals and manufacture a duplicate continuation.
244
+ AND goal.continuation_wake_revision = 0
245
+ AND goal.continuation_observed_revision = 0
246
+ AND session.status <> 'cancelled'
247
+ AND session.active_turn_id IS NULL
248
+ AND NOT EXISTS (
249
+ SELECT 1
250
+ FROM "session_turns" AS turn
251
+ WHERE turn.workspace_id = goal.workspace_id
252
+ AND turn.session_id = goal.session_id
253
+ AND turn.status IN ('queued', 'running', 'requires_action', 'recovering', 'waiting_capacity')
254
+ )
255
+ AND NOT EXISTS (
256
+ SELECT 1
257
+ FROM "codex_capacity_waiters" AS waiter
258
+ WHERE waiter.workspace_id = goal.workspace_id
259
+ AND waiter.session_id = goal.session_id
260
+ AND waiter.status = 'waiting'
261
+ )
262
+ ), ancestry AS (
263
+ SELECT candidate.id AS goal_id, session.workspace_id,
264
+ session.id AS session_id, session.parent_session_id,
265
+ session.direct_control_state, session.direct_pause_revision,
266
+ session.subtree_run_override_revision,
267
+ 0::integer AS depth, ARRAY[session.id]::uuid[] AS path, false AS cycle
268
+ FROM candidates AS candidate
269
+ JOIN "sessions" AS session
270
+ ON session.workspace_id = candidate.workspace_id
271
+ AND session.id = candidate.session_id
272
+ UNION ALL
273
+ SELECT child.goal_id, parent.workspace_id,
274
+ parent.id, parent.parent_session_id,
275
+ parent.direct_control_state, parent.direct_pause_revision,
276
+ parent.subtree_run_override_revision,
277
+ child.depth + 1, child.path || parent.id,
278
+ parent.id = ANY(child.path)
279
+ FROM ancestry AS child
280
+ JOIN "sessions" AS parent
281
+ ON parent.workspace_id = child.workspace_id
282
+ AND parent.id = child.parent_session_id
283
+ WHERE child.parent_session_id IS NOT NULL
284
+ AND NOT child.cycle
285
+ AND child.depth < 10000
286
+ ), repairable AS (
287
+ SELECT candidate.*
288
+ FROM candidates AS candidate
289
+ JOIN "workspace_inference_controls" AS control
290
+ ON control.workspace_id = candidate.workspace_id
291
+ WHERE EXISTS (
292
+ SELECT 1
293
+ FROM ancestry AS root
294
+ WHERE root.goal_id = candidate.id
295
+ AND root.parent_session_id IS NULL
296
+ AND NOT root.cycle
297
+ )
298
+ AND NOT EXISTS (
299
+ SELECT 1
300
+ FROM ancestry AS invalid
301
+ WHERE invalid.goal_id = candidate.id AND invalid.cycle
302
+ )
303
+ AND NOT EXISTS (
304
+ SELECT 1
305
+ FROM ancestry AS pause
306
+ WHERE pause.goal_id = candidate.id
307
+ AND pause.direct_control_state = 'paused'
308
+ AND NOT EXISTS (
309
+ SELECT 1
310
+ FROM ancestry AS override
311
+ WHERE override.goal_id = candidate.id
312
+ AND override.depth < pause.depth
313
+ AND override.subtree_run_override_revision > pause.direct_pause_revision
314
+ )
315
+ )
316
+ AND (
317
+ control.workspace_state = 'active'
318
+ OR (
319
+ control.workspace_state = 'paused'
320
+ AND EXISTS (
321
+ SELECT 1
322
+ FROM ancestry AS override
323
+ WHERE override.goal_id = candidate.id
324
+ AND override.subtree_run_override_revision > control.workspace_pause_revision
325
+ )
326
+ )
327
+ )
328
+ ), armed AS (
329
+ UPDATE "session_goals" AS goal
330
+ SET "continuation_wake_revision" = goal.continuation_wake_revision + 1,
331
+ "updated_at" = now()
332
+ FROM repairable
333
+ WHERE goal.id = repairable.id
334
+ RETURNING repairable.account_id, repairable.workspace_id,
335
+ repairable.session_id, repairable.workflow_id
336
+ )
337
+ INSERT INTO "session_workflow_wake_outbox" (
338
+ "session_id", "account_id", "workspace_id", "temporal_workflow_id",
339
+ "wake_revision", "delivered_revision", "reason", "attempts",
340
+ "next_attempt_at", "created_at", "updated_at"
341
+ )
342
+ SELECT session_id, account_id, workspace_id, workflow_id,
343
+ 1, 0, 'goal_obligation_backfill', 0, now(), now(), now()
344
+ FROM armed
345
+ ON CONFLICT ("session_id") DO UPDATE SET
346
+ "temporal_workflow_id" = EXCLUDED."temporal_workflow_id",
347
+ "wake_revision" = "session_workflow_wake_outbox"."wake_revision" + 1,
348
+ "reason" = EXCLUDED."reason",
349
+ "attempts" = 0,
350
+ "next_attempt_at" = LEAST("session_workflow_wake_outbox"."next_attempt_at", EXCLUDED."next_attempt_at"),
351
+ "last_error" = NULL,
352
+ "updated_at" = now();
353
+
354
+ ALTER TABLE "session_goals" FORCE ROW LEVEL SECURITY;
355
+ ALTER TABLE "session_system_updates" FORCE ROW LEVEL SECURITY;
356
+ ALTER TABLE "session_workflow_wake_outbox" FORCE ROW LEVEL SECURITY;
357
+ ALTER TABLE "sessions" FORCE ROW LEVEL SECURITY;
358
+ ALTER TABLE "workspace_inference_controls" FORCE ROW LEVEL SECURITY;
359
+ ALTER TABLE "session_turns" FORCE ROW LEVEL SECURITY;
360
+ ALTER TABLE "codex_capacity_waiters" FORCE ROW LEVEL SECURITY;
@@ -0,0 +1,11 @@
1
+ -- deployment-mode: rolling
2
+
3
+ -- A goal_update operation belongs to the target goal/session, not to one
4
+ -- replaceable worker attempt. The receipt retains its original attempt FK for
5
+ -- audit, while this partial identity lets a recovered attempt reconcile the
6
+ -- exact committed result.
7
+ CREATE UNIQUE INDEX "session_command_receipts_goal_update_operation_uq"
8
+ ON "session_command_receipts" (
9
+ "workspace_id", "action", "target_session_id", "operation_key"
10
+ )
11
+ WHERE "action" = 'goal.update';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "0.10.7",
3
+ "version": "0.12.1",
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": {
@@ -50,8 +50,8 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@opengeni/codex": "^0.2.7",
53
- "@opengeni/config": "^0.6.9",
54
- "@opengeni/contracts": "^0.18.0",
53
+ "@opengeni/config": "^0.7.1",
54
+ "@opengeni/contracts": "^0.19.0",
55
55
  "@opengeni/network": "^0.1.1",
56
56
  "drizzle-orm": "^0.45.2",
57
57
  "postgres": "^3.4.7"