@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.
- package/dist/{chunk-P6PKXY5W.js → chunk-VUKRIBO5.js} +485 -12
- package/dist/chunk-VUKRIBO5.js.map +1 -0
- package/dist/{chunk-KW526IJA.js → chunk-Y5WZZVQK.js} +80 -4
- package/dist/chunk-Y5WZZVQK.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +5114 -2085
- package/dist/index.js.map +1 -1
- package/dist/migrate.d.ts +6 -3
- package/dist/migrate.js +1 -1
- package/dist/provision-roles.d.ts +720 -63
- package/dist/{schema-CqkzrBRS.d.ts → schema-BejThLcd.d.ts} +2965 -1194
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +17 -1
- package/drizzle/0109_nested_agent_depth_expand.sql +42 -0
- package/drizzle/0110_nested_agent_depth_boundary.sql +480 -0
- package/drizzle/0111_nested_agent_depth_backfill.sql +49 -0
- package/drizzle/0112_nested_agent_depth_contract.sql +38 -0
- package/drizzle/0113_nested_agent_depth_validate.sql +13 -0
- package/drizzle/0114_nested_agent_depth_contract.sql +49 -0
- package/drizzle/0115_nested_agent_depth_validate.sql +11 -0
- package/drizzle/0116_nested_agent_depth_index.sql +4 -0
- package/drizzle/0117_sandbox_recovery_generations.sql +699 -0
- package/drizzle/0118_new_session_drafts.sql +59 -0
- package/drizzle/0119_pending_tool_output_policy.sql +5 -0
- package/drizzle/0120_durable_goal_wake.sql +360 -0
- package/drizzle/0121_goal_update_idempotency.sql +11 -0
- package/package.json +3 -3
- package/src/index.ts +5961 -1240
- package/src/migrate.ts +131 -2
- package/src/new-session-drafts.ts +144 -0
- package/src/schema.ts +519 -15
- package/src/session-control.ts +42 -18
- package/src/session-tool-call-settlement.ts +6 -1
- package/dist/chunk-KW526IJA.js.map +0 -1
- 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.
|
|
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.
|
|
54
|
-
"@opengeni/contracts": "^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"
|