@opengeni/db 0.14.3 → 0.17.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-Y5WZZVQK.js → chunk-SK7YYHBI.js} +5 -2
- package/dist/chunk-SK7YYHBI.js.map +1 -0
- package/dist/{chunk-ELMUCYZF.js → chunk-T6RSJT6C.js} +202 -43
- package/dist/chunk-T6RSJT6C.js.map +1 -0
- package/dist/chunk-TLAC622R.js +4990 -0
- package/dist/chunk-TLAC622R.js.map +1 -0
- package/dist/codex-token-resolver.d.ts +64 -0
- package/dist/connection-token-resolver.d.ts +90 -0
- package/dist/environment-crypto.d.ts +11 -0
- package/dist/event-payload-sanitizer.d.ts +32 -0
- package/dist/index.d.ts +6374 -10
- package/dist/index.js +8075 -2776
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +157 -0
- package/dist/memory-domain.d.ts +44 -0
- package/dist/migrate.d.ts +5 -7
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +49 -0
- package/dist/persistence-errors.d.ts +69 -0
- package/dist/preference-registry-schema.d.ts +1147 -0
- package/dist/preference-registry.d.ts +878 -0
- package/dist/provision-roles.d.ts +4 -6489
- package/dist/provision-roles.js +1 -1
- package/dist/role-relationships.d.ts +35 -0
- package/dist/runtime-posture-cli.d.ts +1 -0
- package/dist/runtime-posture.d.ts +103 -0
- package/dist/schema.d.ts +25330 -3
- package/dist/schema.js +31 -1
- package/dist/session-control.d.ts +331 -0
- package/dist/session-queue-commands.d.ts +186 -0
- package/dist/session-tool-call-settlement.d.ts +32 -0
- package/dist/turn-initiator.d.ts +28 -0
- package/dist/workspace-artifacts.d.ts +62 -0
- package/dist/workspace-instruction-policies-schema.d.ts +735 -0
- package/dist/workspace-instruction-policies.d.ts +64 -0
- package/drizzle/0136_unified_session_tool_policy.sql +1 -1
- package/drizzle/0137_preference_registry.sql +1347 -0
- package/drizzle/0138_sandbox_checkpoint_artifacts_and_deadlines.sql +1074 -0
- package/drizzle/0139_codex_provider_artifact_invalidations.sql +51 -0
- package/drizzle/0140_sandbox_restore_and_reaper_fences.sql +276 -0
- package/drizzle/0142_sandbox_archive_capture_gate.sql +80 -0
- package/drizzle/0143_session_codex_compaction_mode.sql +20 -0
- package/drizzle/0144_sandbox_viewer_force_drain_gate.sql +95 -0
- package/drizzle/0145_model_call_facts.sql +96 -0
- package/drizzle/0146_slack_bot_delete_idempotency.sql +105 -0
- package/drizzle/0147_draft_latency_mode.sql +12 -0
- package/drizzle/0148_session_turn_latency_mode.sql +12 -0
- package/drizzle/0149_workspace_artifacts.sql +348 -0
- package/drizzle/0150_slack_task_interactions.sql +344 -0
- package/package.json +6 -6
- package/src/index.ts +5766 -275
- package/src/insights.ts +837 -0
- package/src/migrate.ts +9 -1
- package/src/new-session-drafts.ts +4 -0
- package/src/preference-registry-schema.ts +170 -0
- package/src/preference-registry.ts +1220 -0
- package/src/provision-roles.ts +95 -22
- package/src/role-relationships.ts +132 -0
- package/src/runtime-posture.ts +47 -27
- package/src/schema.ts +875 -19
- package/src/session-queue-commands.ts +39 -6
- package/src/workspace-artifacts.ts +751 -0
- package/dist/chunk-DW24CKCT.js +0 -4046
- package/dist/chunk-DW24CKCT.js.map +0 -1
- package/dist/chunk-ELMUCYZF.js.map +0 -1
- package/dist/chunk-Y5WZZVQK.js.map +0 -1
- package/dist/schema-DhkRhcuQ.d.ts +0 -22666
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Preserve durable conversation truth while allowing an exact Codex provider
|
|
3
|
+
-- rejection to invalidate only its opaque, provider-bound reasoning artifact.
|
|
4
|
+
-- The item itself remains immutable/auditable; the model read path omits the
|
|
5
|
+
-- rejected identity on the next fenced attempt.
|
|
6
|
+
|
|
7
|
+
ALTER TABLE "session_history_items"
|
|
8
|
+
ADD COLUMN "provider_artifact_invalidated_at" timestamptz,
|
|
9
|
+
ADD COLUMN "provider_artifact_invalidation_reason" text,
|
|
10
|
+
ADD COLUMN "provider_artifact_invalidated_by_attempt_id" uuid,
|
|
11
|
+
ADD CONSTRAINT "session_history_items_provider_artifact_invalidation_shape_chk"
|
|
12
|
+
CHECK (
|
|
13
|
+
(
|
|
14
|
+
"provider_artifact_invalidated_at" IS NULL
|
|
15
|
+
AND "provider_artifact_invalidation_reason" IS NULL
|
|
16
|
+
AND "provider_artifact_invalidated_by_attempt_id" IS NULL
|
|
17
|
+
)
|
|
18
|
+
OR
|
|
19
|
+
(
|
|
20
|
+
"provider_artifact_invalidated_at" IS NOT NULL
|
|
21
|
+
AND "provider_artifact_invalidation_reason" = 'encrypted_content_rejected'
|
|
22
|
+
AND "provider_artifact_invalidated_by_attempt_id" IS NOT NULL
|
|
23
|
+
AND "item" ->> 'type' IN ('reasoning', 'compaction')
|
|
24
|
+
)
|
|
25
|
+
) NOT VALID;
|
|
26
|
+
|
|
27
|
+
ALTER TABLE "agent_run_states"
|
|
28
|
+
ADD COLUMN "provider_artifact_invalidated_at" timestamptz,
|
|
29
|
+
ADD COLUMN "provider_artifact_invalidation_reason" text,
|
|
30
|
+
ADD COLUMN "provider_artifact_invalidated_by_attempt_id" uuid,
|
|
31
|
+
ADD CONSTRAINT "agent_run_states_provider_artifact_invalidation_shape_chk"
|
|
32
|
+
CHECK (
|
|
33
|
+
(
|
|
34
|
+
"provider_artifact_invalidated_at" IS NULL
|
|
35
|
+
AND "provider_artifact_invalidation_reason" IS NULL
|
|
36
|
+
AND "provider_artifact_invalidated_by_attempt_id" IS NULL
|
|
37
|
+
)
|
|
38
|
+
OR
|
|
39
|
+
(
|
|
40
|
+
"provider_artifact_invalidated_at" IS NOT NULL
|
|
41
|
+
AND "provider_artifact_invalidation_reason" = 'encrypted_content_rejected'
|
|
42
|
+
AND "provider_artifact_invalidated_by_attempt_id" IS NOT NULL
|
|
43
|
+
)
|
|
44
|
+
) NOT VALID;
|
|
45
|
+
|
|
46
|
+
DO $$
|
|
47
|
+
BEGIN
|
|
48
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
49
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO opengeni_app;
|
|
50
|
+
END IF;
|
|
51
|
+
END $$;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- A legacy Modal checkpoint is immutable at the generation at which it was
|
|
3
|
+
-- adopted. Restoring it and then writing to the live workspace must advance
|
|
4
|
+
-- workspace_generation without invalidating that checkpoint. Migration 0138
|
|
5
|
+
-- accidentally required archive_generation = workspace_generation forever,
|
|
6
|
+
-- so the first post-restore mutation failed the deferred scope trigger.
|
|
7
|
+
--
|
|
8
|
+
-- The global lease reaper also used one statement snapshot to count holders
|
|
9
|
+
-- before waiting on a concurrently acquired lease row. PostgreSQL could then
|
|
10
|
+
-- apply that stale zero count after the acquire committed, changing a live
|
|
11
|
+
-- lease to draining underneath its new owner. The replacement reaper locks a
|
|
12
|
+
-- bounded, fair batch with SKIP LOCKED first and touches only that exact batch.
|
|
13
|
+
|
|
14
|
+
DO $checkpoint_ref_validator$
|
|
15
|
+
DECLARE data_schema text := current_schema();
|
|
16
|
+
BEGIN
|
|
17
|
+
EXECUTE format($create$
|
|
18
|
+
CREATE OR REPLACE FUNCTION opengeni_private.validate_sandbox_checkpoint_refs()
|
|
19
|
+
RETURNS trigger
|
|
20
|
+
LANGUAGE plpgsql
|
|
21
|
+
SECURITY DEFINER
|
|
22
|
+
SET search_path = pg_catalog
|
|
23
|
+
AS $function$
|
|
24
|
+
DECLARE checkpoint %1$I.sandbox_checkpoint_artifacts%%ROWTYPE;
|
|
25
|
+
DECLARE legacy_archive_rebound boolean := false;
|
|
26
|
+
BEGIN
|
|
27
|
+
IF TG_OP = 'UPDATE' THEN
|
|
28
|
+
legacy_archive_rebound :=
|
|
29
|
+
OLD.current_checkpoint_artifact_id = NEW.current_checkpoint_artifact_id
|
|
30
|
+
AND OLD.archive_generation IS DISTINCT FROM NEW.archive_generation;
|
|
31
|
+
END IF;
|
|
32
|
+
|
|
33
|
+
IF NEW.current_checkpoint_artifact_id IS NOT NULL THEN
|
|
34
|
+
SELECT * INTO checkpoint
|
|
35
|
+
FROM %1$I.sandbox_checkpoint_artifacts
|
|
36
|
+
WHERE id = NEW.current_checkpoint_artifact_id;
|
|
37
|
+
IF NOT FOUND
|
|
38
|
+
OR checkpoint.account_id <> NEW.account_id
|
|
39
|
+
OR checkpoint.workspace_id <> NEW.workspace_id
|
|
40
|
+
OR checkpoint.sandbox_group_id <> NEW.sandbox_group_id
|
|
41
|
+
OR checkpoint.source_lease_id <> NEW.id
|
|
42
|
+
OR checkpoint.provider_backend <> NEW.backend
|
|
43
|
+
OR (
|
|
44
|
+
checkpoint.provenance = 'native_capture'
|
|
45
|
+
AND checkpoint.source_workspace_generation
|
|
46
|
+
IS DISTINCT FROM NEW.archive_generation
|
|
47
|
+
)
|
|
48
|
+
OR (
|
|
49
|
+
checkpoint.provenance = 'legacy_provider_adopted'
|
|
50
|
+
AND (
|
|
51
|
+
NEW.archive_generation IS NULL
|
|
52
|
+
OR legacy_archive_rebound
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
OR checkpoint.archive_base64
|
|
56
|
+
IS DISTINCT FROM NEW.resume_state #>> '{sessionState,workspaceArchive}'
|
|
57
|
+
OR checkpoint.descriptor_revision
|
|
58
|
+
IS DISTINCT FROM NEW.resume_state #>> '{sessionState,workspaceArchiveMeta,revision}'
|
|
59
|
+
OR checkpoint.state <> 'current'
|
|
60
|
+
THEN
|
|
61
|
+
RAISE EXCEPTION 'current checkpoint artifact does not match its exact lease scope'
|
|
62
|
+
USING ERRCODE = '23514';
|
|
63
|
+
END IF;
|
|
64
|
+
END IF;
|
|
65
|
+
|
|
66
|
+
IF NEW.previous_checkpoint_artifact_id IS NOT NULL THEN
|
|
67
|
+
SELECT * INTO checkpoint
|
|
68
|
+
FROM %1$I.sandbox_checkpoint_artifacts
|
|
69
|
+
WHERE id = NEW.previous_checkpoint_artifact_id;
|
|
70
|
+
IF NOT FOUND
|
|
71
|
+
OR checkpoint.account_id <> NEW.account_id
|
|
72
|
+
OR checkpoint.workspace_id <> NEW.workspace_id
|
|
73
|
+
OR checkpoint.sandbox_group_id <> NEW.sandbox_group_id
|
|
74
|
+
OR checkpoint.source_lease_id <> NEW.id
|
|
75
|
+
OR checkpoint.provider_backend <> NEW.backend
|
|
76
|
+
OR checkpoint.archive_base64
|
|
77
|
+
IS DISTINCT FROM NEW.resume_state #>> '{sessionState,workspaceArchivePrev}'
|
|
78
|
+
OR checkpoint.descriptor_revision
|
|
79
|
+
IS DISTINCT FROM NEW.resume_state #>> '{sessionState,workspaceArchivePrevMeta,revision}'
|
|
80
|
+
OR checkpoint.state <> 'previous'
|
|
81
|
+
THEN
|
|
82
|
+
RAISE EXCEPTION 'previous checkpoint artifact does not match its exact lease scope'
|
|
83
|
+
USING ERRCODE = '23514';
|
|
84
|
+
END IF;
|
|
85
|
+
END IF;
|
|
86
|
+
RETURN NEW;
|
|
87
|
+
END;
|
|
88
|
+
$function$;
|
|
89
|
+
$create$, data_schema);
|
|
90
|
+
END
|
|
91
|
+
$checkpoint_ref_validator$;
|
|
92
|
+
|
|
93
|
+
DO $bounded_lock_first_reaper$
|
|
94
|
+
DECLARE data_schema text := current_schema();
|
|
95
|
+
BEGIN
|
|
96
|
+
EXECUTE format($create$
|
|
97
|
+
CREATE OR REPLACE FUNCTION opengeni_private.reap_sandbox_leases(
|
|
98
|
+
p_viewer_holder_ttl_ms bigint,
|
|
99
|
+
p_turn_holder_ttl_ms bigint,
|
|
100
|
+
p_idle_grace_ms bigint
|
|
101
|
+
)
|
|
102
|
+
RETURNS TABLE (
|
|
103
|
+
workspace_id uuid,
|
|
104
|
+
sandbox_group_id uuid,
|
|
105
|
+
instance_id text,
|
|
106
|
+
lease_epoch integer
|
|
107
|
+
)
|
|
108
|
+
LANGUAGE plpgsql
|
|
109
|
+
SECURITY DEFINER
|
|
110
|
+
SET search_path = pg_catalog
|
|
111
|
+
AS $function$
|
|
112
|
+
DECLARE locked_ids uuid[];
|
|
113
|
+
BEGIN
|
|
114
|
+
PERFORM pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
|
|
115
|
+
|
|
116
|
+
-- Every holder-creating runtime path locks its lease first. SKIP LOCKED
|
|
117
|
+
-- therefore gives one coherent statement snapshot without ever waiting
|
|
118
|
+
-- behind an acquire and later publishing pre-wait counts. Old/corrupt
|
|
119
|
+
-- cold rows carrying holders are included for bounded repair. Updating
|
|
120
|
+
-- selected rows' updated_at below rotates a backlog fairly.
|
|
121
|
+
SELECT coalesce(
|
|
122
|
+
pg_catalog.array_agg(candidate.id),
|
|
123
|
+
ARRAY[]::uuid[]
|
|
124
|
+
)
|
|
125
|
+
INTO locked_ids
|
|
126
|
+
FROM (
|
|
127
|
+
SELECT lease.id
|
|
128
|
+
FROM %1$I.sandbox_leases lease
|
|
129
|
+
WHERE lease.liveness <> 'cold'
|
|
130
|
+
OR EXISTS (
|
|
131
|
+
SELECT 1
|
|
132
|
+
FROM %1$I.sandbox_lease_holders holder
|
|
133
|
+
WHERE holder.lease_id = lease.id
|
|
134
|
+
)
|
|
135
|
+
ORDER BY lease.updated_at, lease.id
|
|
136
|
+
LIMIT 500
|
|
137
|
+
FOR UPDATE OF lease SKIP LOCKED
|
|
138
|
+
) candidate;
|
|
139
|
+
|
|
140
|
+
IF pg_catalog.cardinality(locked_ids) = 0 THEN
|
|
141
|
+
RETURN;
|
|
142
|
+
END IF;
|
|
143
|
+
|
|
144
|
+
-- A legacy heartbeat/force-drain writer can still hold an individual
|
|
145
|
+
-- holder row during a rolling rollout. Lock stale victims with SKIP
|
|
146
|
+
-- LOCKED as well, avoiding a holder->lease / lease->holder deadlock.
|
|
147
|
+
DELETE FROM %1$I.sandbox_lease_holders holder
|
|
148
|
+
USING (
|
|
149
|
+
SELECT stale.id
|
|
150
|
+
FROM %1$I.sandbox_lease_holders stale
|
|
151
|
+
WHERE stale.lease_id = ANY(locked_ids)
|
|
152
|
+
AND stale.kind IN ('viewer', 'direct')
|
|
153
|
+
AND stale.last_heartbeat_at
|
|
154
|
+
< pg_catalog.now()
|
|
155
|
+
- pg_catalog.make_interval(secs => p_viewer_holder_ttl_ms / 1000.0)
|
|
156
|
+
FOR UPDATE OF stale SKIP LOCKED
|
|
157
|
+
) victim
|
|
158
|
+
WHERE holder.id = victim.id;
|
|
159
|
+
|
|
160
|
+
IF p_turn_holder_ttl_ms > 0 THEN
|
|
161
|
+
DELETE FROM %1$I.sandbox_lease_holders holder
|
|
162
|
+
USING (
|
|
163
|
+
SELECT stale.id
|
|
164
|
+
FROM %1$I.sandbox_lease_holders stale
|
|
165
|
+
WHERE stale.lease_id = ANY(locked_ids)
|
|
166
|
+
AND stale.kind = 'turn'
|
|
167
|
+
AND stale.last_heartbeat_at
|
|
168
|
+
< pg_catalog.now()
|
|
169
|
+
- pg_catalog.make_interval(secs => p_turn_holder_ttl_ms / 1000.0)
|
|
170
|
+
FOR UPDATE OF stale SKIP LOCKED
|
|
171
|
+
) victim
|
|
172
|
+
WHERE holder.id = victim.id;
|
|
173
|
+
END IF;
|
|
174
|
+
|
|
175
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
176
|
+
refcount = counts.total,
|
|
177
|
+
turn_holders = counts.turns,
|
|
178
|
+
viewer_holders = counts.viewers,
|
|
179
|
+
liveness = CASE
|
|
180
|
+
WHEN lease.liveness = 'warm' AND counts.total = 0 AND counts.turns = 0
|
|
181
|
+
THEN 'draining' ELSE lease.liveness END,
|
|
182
|
+
expires_at = CASE
|
|
183
|
+
WHEN lease.liveness = 'warm' AND counts.total = 0 AND counts.turns = 0
|
|
184
|
+
THEN CASE
|
|
185
|
+
WHEN lease.rotation_requested_at IS NOT NULL
|
|
186
|
+
THEN pg_catalog.now() - interval '1 millisecond'
|
|
187
|
+
ELSE pg_catalog.now()
|
|
188
|
+
+ pg_catalog.make_interval(secs => p_idle_grace_ms / 1000.0)
|
|
189
|
+
END
|
|
190
|
+
ELSE lease.expires_at END,
|
|
191
|
+
updated_at = pg_catalog.now()
|
|
192
|
+
FROM (
|
|
193
|
+
SELECT candidate.id,
|
|
194
|
+
pg_catalog.count(holder.id)::int AS total,
|
|
195
|
+
pg_catalog.count(holder.id)
|
|
196
|
+
FILTER (WHERE holder.kind = 'turn')::int AS turns,
|
|
197
|
+
pg_catalog.count(holder.id)
|
|
198
|
+
FILTER (WHERE holder.kind = 'viewer')::int AS viewers
|
|
199
|
+
FROM pg_catalog.unnest(locked_ids) candidate(id)
|
|
200
|
+
LEFT JOIN %1$I.sandbox_lease_holders holder
|
|
201
|
+
ON holder.lease_id = candidate.id
|
|
202
|
+
GROUP BY candidate.id
|
|
203
|
+
) counts
|
|
204
|
+
WHERE lease.id = counts.id;
|
|
205
|
+
|
|
206
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
207
|
+
liveness = 'cold',
|
|
208
|
+
instance_id = null,
|
|
209
|
+
lease_epoch = lease.lease_epoch + 1,
|
|
210
|
+
resume_state = opengeni_private.warming_reset_resume_state_v2(
|
|
211
|
+
lease.backend,
|
|
212
|
+
lease.resume_backend_id,
|
|
213
|
+
lease.resume_state,
|
|
214
|
+
lease.workspace_generation,
|
|
215
|
+
lease.archive_generation,
|
|
216
|
+
pg_catalog.clock_timestamp()
|
|
217
|
+
),
|
|
218
|
+
resume_backend_id = CASE
|
|
219
|
+
WHEN coalesce(
|
|
220
|
+
lease.resume_state #>> '{sessionState,workspaceArchive}', ''
|
|
221
|
+
) <> ''
|
|
222
|
+
OR coalesce(
|
|
223
|
+
lease.resume_state #>> '{sessionState,workspaceArchivePrev}', ''
|
|
224
|
+
) <> ''
|
|
225
|
+
THEN coalesce(lease.resume_backend_id, lease.backend)
|
|
226
|
+
ELSE null END,
|
|
227
|
+
data_plane_url = null,
|
|
228
|
+
terminal_data_plane_url = null,
|
|
229
|
+
provider_created_at = null,
|
|
230
|
+
provider_deadline_at = null,
|
|
231
|
+
rotation_requested_at = null,
|
|
232
|
+
rotation_reason = null,
|
|
233
|
+
updated_at = pg_catalog.now()
|
|
234
|
+
WHERE lease.id = ANY(locked_ids)
|
|
235
|
+
AND lease.liveness = 'warming'
|
|
236
|
+
AND lease.expires_at < pg_catalog.now()
|
|
237
|
+
AND lease.instance_id IS NULL;
|
|
238
|
+
|
|
239
|
+
UPDATE %1$I.sandbox_leases lease SET
|
|
240
|
+
liveness = 'draining',
|
|
241
|
+
refcount = 0,
|
|
242
|
+
turn_holders = 0,
|
|
243
|
+
viewer_holders = 0,
|
|
244
|
+
data_plane_url = null,
|
|
245
|
+
terminal_data_plane_url = null,
|
|
246
|
+
lease_epoch = lease.lease_epoch + 1,
|
|
247
|
+
expires_at = pg_catalog.now() - interval '1 millisecond',
|
|
248
|
+
updated_at = pg_catalog.now()
|
|
249
|
+
WHERE lease.id = ANY(locked_ids)
|
|
250
|
+
AND lease.liveness = 'warming'
|
|
251
|
+
AND lease.expires_at < pg_catalog.now()
|
|
252
|
+
AND lease.instance_id IS NOT NULL;
|
|
253
|
+
|
|
254
|
+
RETURN QUERY
|
|
255
|
+
SELECT lease.workspace_id, lease.sandbox_group_id,
|
|
256
|
+
lease.instance_id, lease.lease_epoch
|
|
257
|
+
FROM %1$I.sandbox_leases lease
|
|
258
|
+
WHERE lease.id = ANY(locked_ids)
|
|
259
|
+
AND lease.liveness = 'draining'
|
|
260
|
+
AND lease.expires_at < pg_catalog.now()
|
|
261
|
+
AND lease.refcount = 0;
|
|
262
|
+
END;
|
|
263
|
+
$function$;
|
|
264
|
+
$create$, data_schema);
|
|
265
|
+
END
|
|
266
|
+
$bounded_lock_first_reaper$;
|
|
267
|
+
|
|
268
|
+
DO $grants$
|
|
269
|
+
BEGIN
|
|
270
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
271
|
+
GRANT EXECUTE ON FUNCTION
|
|
272
|
+
opengeni_private.reap_sandbox_leases(bigint, bigint, bigint)
|
|
273
|
+
TO opengeni_app;
|
|
274
|
+
END IF;
|
|
275
|
+
END
|
|
276
|
+
$grants$;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- A provider-native workspace snapshot pauses the Modal container while it is
|
|
3
|
+
-- captured. The previous generation preflight prevented a stale snapshot from
|
|
4
|
+
-- being published, but it did not prevent a new provider command from being
|
|
5
|
+
-- admitted after preflight and rejected by Modal while the container was
|
|
6
|
+
-- paused. Activate one durable lease-local capture gate that both snapshot
|
|
7
|
+
-- paths claim before provider I/O and every holder/mutation path honors.
|
|
8
|
+
--
|
|
9
|
+
-- Old application workers do not honor this gate. Stop every opengeni_app
|
|
10
|
+
-- session before activation and never restart a pre-0142 image afterward.
|
|
11
|
+
|
|
12
|
+
SET LOCAL lock_timeout = '5s';
|
|
13
|
+
SET LOCAL statement_timeout = '30min';
|
|
14
|
+
|
|
15
|
+
DO $maintenance_preflight_guard$
|
|
16
|
+
BEGIN
|
|
17
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app')
|
|
18
|
+
AND EXISTS (
|
|
19
|
+
SELECT 1
|
|
20
|
+
FROM pg_stat_activity
|
|
21
|
+
WHERE datname = current_database()
|
|
22
|
+
AND usename = 'opengeni_app'
|
|
23
|
+
AND pid <> pg_backend_pid()
|
|
24
|
+
)
|
|
25
|
+
THEN
|
|
26
|
+
RAISE EXCEPTION
|
|
27
|
+
'sandbox archive-capture gate activation requires all opengeni_app sessions to be stopped'
|
|
28
|
+
USING ERRCODE = '55000';
|
|
29
|
+
END IF;
|
|
30
|
+
END
|
|
31
|
+
$maintenance_preflight_guard$;
|
|
32
|
+
|
|
33
|
+
LOCK TABLE sandbox_leases IN ACCESS EXCLUSIVE MODE;
|
|
34
|
+
|
|
35
|
+
DO $maintenance_guard$
|
|
36
|
+
BEGIN
|
|
37
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app')
|
|
38
|
+
AND EXISTS (
|
|
39
|
+
SELECT 1
|
|
40
|
+
FROM pg_stat_activity
|
|
41
|
+
WHERE datname = current_database()
|
|
42
|
+
AND usename = 'opengeni_app'
|
|
43
|
+
AND pid <> pg_backend_pid()
|
|
44
|
+
)
|
|
45
|
+
THEN
|
|
46
|
+
RAISE EXCEPTION
|
|
47
|
+
'sandbox archive-capture gate activation requires all opengeni_app sessions to be stopped'
|
|
48
|
+
USING ERRCODE = '55000';
|
|
49
|
+
END IF;
|
|
50
|
+
END
|
|
51
|
+
$maintenance_guard$;
|
|
52
|
+
|
|
53
|
+
ALTER TABLE sandbox_leases
|
|
54
|
+
ADD COLUMN archive_capture_id uuid,
|
|
55
|
+
ADD COLUMN archive_capture_generation integer,
|
|
56
|
+
ADD COLUMN archive_capture_started_at timestamptz,
|
|
57
|
+
ADD COLUMN archive_capture_deadline_at timestamptz;
|
|
58
|
+
|
|
59
|
+
ALTER TABLE sandbox_leases
|
|
60
|
+
ADD CONSTRAINT sandbox_leases_archive_capture_check
|
|
61
|
+
CHECK (
|
|
62
|
+
(
|
|
63
|
+
archive_capture_id IS NULL
|
|
64
|
+
AND archive_capture_generation IS NULL
|
|
65
|
+
AND archive_capture_started_at IS NULL
|
|
66
|
+
AND archive_capture_deadline_at IS NULL
|
|
67
|
+
)
|
|
68
|
+
OR
|
|
69
|
+
(
|
|
70
|
+
archive_capture_id IS NOT NULL
|
|
71
|
+
AND archive_capture_generation IS NOT NULL
|
|
72
|
+
AND archive_capture_generation = workspace_generation
|
|
73
|
+
AND archive_capture_started_at IS NOT NULL
|
|
74
|
+
AND archive_capture_deadline_at > archive_capture_started_at
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
CREATE INDEX sandbox_leases_archive_capture_deadline_idx
|
|
79
|
+
ON sandbox_leases (archive_capture_deadline_at, id)
|
|
80
|
+
WHERE archive_capture_id IS NOT NULL;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Freeze each session's Codex compaction strategy at create time.
|
|
3
|
+
-- Existing sessions stay on portable plaintext compaction so mid-session
|
|
4
|
+
-- provider switches remain available; new Codex sessions default to remote_v2
|
|
5
|
+
-- unless the workspace opts into portable.
|
|
6
|
+
|
|
7
|
+
ALTER TABLE "sessions"
|
|
8
|
+
ADD COLUMN IF NOT EXISTS "codex_compaction_mode" text NOT NULL DEFAULT 'portable';
|
|
9
|
+
|
|
10
|
+
ALTER TABLE "sessions"
|
|
11
|
+
DROP CONSTRAINT IF EXISTS "sessions_codex_compaction_mode_chk";
|
|
12
|
+
|
|
13
|
+
ALTER TABLE "sessions"
|
|
14
|
+
ADD CONSTRAINT "sessions_codex_compaction_mode_chk"
|
|
15
|
+
CHECK ("codex_compaction_mode" IN ('remote_v2', 'portable'));
|
|
16
|
+
|
|
17
|
+
UPDATE "sessions"
|
|
18
|
+
SET "codex_compaction_mode" = 'portable'
|
|
19
|
+
WHERE "codex_compaction_mode" IS DISTINCT FROM 'portable'
|
|
20
|
+
AND "codex_compaction_mode" IS DISTINCT FROM 'remote_v2';
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- A workspace that has exhausted its managed-credit balance or monthly warm
|
|
3
|
+
-- allowance must not let an open dashboard immediately re-arm a viewer-only
|
|
4
|
+
-- sandbox during the drain grace. Persist that admission intent independently
|
|
5
|
+
-- of any one lease so it survives provider teardown and a cold successor row.
|
|
6
|
+
|
|
7
|
+
SET LOCAL lock_timeout = '5s';
|
|
8
|
+
-- Old application processes do not honor this new workspace gate. Activate it
|
|
9
|
+
-- only while the maintenance operator has stopped every opengeni_app session.
|
|
10
|
+
SET LOCAL statement_timeout = '30min';
|
|
11
|
+
|
|
12
|
+
DO $maintenance_preflight_guard$
|
|
13
|
+
BEGIN
|
|
14
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app')
|
|
15
|
+
AND EXISTS (
|
|
16
|
+
SELECT 1
|
|
17
|
+
FROM pg_stat_activity
|
|
18
|
+
WHERE datname = current_database()
|
|
19
|
+
AND usename = 'opengeni_app'
|
|
20
|
+
AND pid <> pg_backend_pid()
|
|
21
|
+
)
|
|
22
|
+
THEN
|
|
23
|
+
RAISE EXCEPTION
|
|
24
|
+
'sandbox viewer force-drain gate activation requires all opengeni_app sessions to be stopped'
|
|
25
|
+
USING ERRCODE = '55000';
|
|
26
|
+
END IF;
|
|
27
|
+
END
|
|
28
|
+
$maintenance_preflight_guard$;
|
|
29
|
+
|
|
30
|
+
LOCK TABLE workspaces IN ACCESS EXCLUSIVE MODE;
|
|
31
|
+
|
|
32
|
+
DO $maintenance_guard$
|
|
33
|
+
BEGIN
|
|
34
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app')
|
|
35
|
+
AND EXISTS (
|
|
36
|
+
SELECT 1
|
|
37
|
+
FROM pg_stat_activity
|
|
38
|
+
WHERE datname = current_database()
|
|
39
|
+
AND usename = 'opengeni_app'
|
|
40
|
+
AND pid <> pg_backend_pid()
|
|
41
|
+
)
|
|
42
|
+
THEN
|
|
43
|
+
RAISE EXCEPTION
|
|
44
|
+
'sandbox viewer force-drain gate activation requires all opengeni_app sessions to be stopped'
|
|
45
|
+
USING ERRCODE = '55000';
|
|
46
|
+
END IF;
|
|
47
|
+
END
|
|
48
|
+
$maintenance_guard$;
|
|
49
|
+
|
|
50
|
+
ALTER TABLE workspaces
|
|
51
|
+
ADD COLUMN sandbox_viewer_force_drain_reason text,
|
|
52
|
+
ADD COLUMN sandbox_viewer_force_drain_requested_at timestamptz;
|
|
53
|
+
|
|
54
|
+
ALTER TABLE workspaces
|
|
55
|
+
ADD CONSTRAINT workspaces_sandbox_viewer_force_drain_check
|
|
56
|
+
CHECK (
|
|
57
|
+
(
|
|
58
|
+
sandbox_viewer_force_drain_reason IS NULL
|
|
59
|
+
AND sandbox_viewer_force_drain_requested_at IS NULL
|
|
60
|
+
)
|
|
61
|
+
OR
|
|
62
|
+
(
|
|
63
|
+
sandbox_viewer_force_drain_reason IN ('balance', 'warm_cap')
|
|
64
|
+
AND sandbox_viewer_force_drain_requested_at IS NOT NULL
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
CREATE INDEX workspaces_sandbox_viewer_force_drain_idx
|
|
69
|
+
ON workspaces (id)
|
|
70
|
+
WHERE sandbox_viewer_force_drain_reason IS NOT NULL;
|
|
71
|
+
|
|
72
|
+
-- The global sandbox reaper must keep reevaluating a persisted admission gate
|
|
73
|
+
-- after every affected lease becomes cold. Returning workspace identity only
|
|
74
|
+
-- keeps billing values and tenant data behind their existing scoped reads.
|
|
75
|
+
CREATE OR REPLACE FUNCTION opengeni_private.list_sandbox_viewer_force_drain_workspaces()
|
|
76
|
+
RETURNS TABLE (workspace_id uuid)
|
|
77
|
+
LANGUAGE sql
|
|
78
|
+
SECURITY DEFINER
|
|
79
|
+
-- EMBED-SAFE: inherit the caller's target-schema search_path, matching the
|
|
80
|
+
-- existing cross-workspace sandbox inventory functions.
|
|
81
|
+
AS $$
|
|
82
|
+
SELECT W.id
|
|
83
|
+
FROM workspaces W
|
|
84
|
+
WHERE W.sandbox_viewer_force_drain_reason IS NOT NULL
|
|
85
|
+
ORDER BY W.id;
|
|
86
|
+
$$;
|
|
87
|
+
|
|
88
|
+
DO $$
|
|
89
|
+
BEGIN
|
|
90
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
91
|
+
GRANT EXECUTE
|
|
92
|
+
ON FUNCTION opengeni_private.list_sandbox_viewer_force_drain_workspaces()
|
|
93
|
+
TO opengeni_app;
|
|
94
|
+
END IF;
|
|
95
|
+
END $$;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Additive Workspace Insights fact table for per-model-call tokens/cache/provider
|
|
3
|
+
-- pivots. Written only after an authoritative agent.model.usage emit; never widens
|
|
4
|
+
-- the billing usage_events ledger. FORCE RLS matches usage_events isolation.
|
|
5
|
+
|
|
6
|
+
SET LOCAL lock_timeout = '5s';
|
|
7
|
+
SET LOCAL statement_timeout = '10min';
|
|
8
|
+
|
|
9
|
+
CREATE TABLE IF NOT EXISTS "model_call_facts" (
|
|
10
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
11
|
+
"account_id" uuid NOT NULL,
|
|
12
|
+
"workspace_id" uuid NOT NULL,
|
|
13
|
+
"session_id" uuid NOT NULL,
|
|
14
|
+
"turn_id" uuid NOT NULL,
|
|
15
|
+
"turn_attempt_id" uuid,
|
|
16
|
+
"source_key" text NOT NULL,
|
|
17
|
+
"provider" text NOT NULL,
|
|
18
|
+
"provider_api" text NOT NULL,
|
|
19
|
+
"model" text NOT NULL,
|
|
20
|
+
"billing_path" text NOT NULL,
|
|
21
|
+
"turn_source" text,
|
|
22
|
+
"initiator_kind" text,
|
|
23
|
+
"initiator_subject_id" text,
|
|
24
|
+
"scheduled_task_id" uuid,
|
|
25
|
+
"input_tokens" bigint,
|
|
26
|
+
"output_tokens" bigint,
|
|
27
|
+
"cached_tokens" bigint,
|
|
28
|
+
"cache_write_tokens" bigint,
|
|
29
|
+
"reasoning_tokens" bigint,
|
|
30
|
+
"total_tokens" bigint,
|
|
31
|
+
"priced_cost_micros" bigint NOT NULL DEFAULT 0,
|
|
32
|
+
"occurred_at" timestamptz NOT NULL,
|
|
33
|
+
"recorded_at" timestamptz NOT NULL DEFAULT now(),
|
|
34
|
+
CONSTRAINT "model_call_facts_workspace_account_fk"
|
|
35
|
+
FOREIGN KEY ("workspace_id", "account_id")
|
|
36
|
+
REFERENCES "workspaces"("id", "account_id") ON DELETE CASCADE,
|
|
37
|
+
CONSTRAINT "model_call_facts_billing_path_check"
|
|
38
|
+
CHECK ("billing_path" IN ('opengeni_credits', 'external')),
|
|
39
|
+
CONSTRAINT "model_call_facts_priced_cost_check"
|
|
40
|
+
CHECK ("priced_cost_micros" >= 0),
|
|
41
|
+
CONSTRAINT "model_call_facts_initiator_check"
|
|
42
|
+
CHECK (
|
|
43
|
+
("initiator_kind" IS NULL AND "initiator_subject_id" IS NULL)
|
|
44
|
+
OR (
|
|
45
|
+
"initiator_kind" IN ('subject', 'service')
|
|
46
|
+
AND "initiator_subject_id" IS NOT NULL
|
|
47
|
+
AND octet_length("initiator_subject_id") BETWEEN 1 AND 1024
|
|
48
|
+
)
|
|
49
|
+
),
|
|
50
|
+
CONSTRAINT "model_call_facts_source_key_bytes_check"
|
|
51
|
+
CHECK (octet_length("source_key") BETWEEN 1 AND 1024),
|
|
52
|
+
CONSTRAINT "model_call_facts_provider_bytes_check"
|
|
53
|
+
CHECK (octet_length("provider") BETWEEN 1 AND 256),
|
|
54
|
+
CONSTRAINT "model_call_facts_provider_api_bytes_check"
|
|
55
|
+
CHECK (octet_length("provider_api") BETWEEN 1 AND 256),
|
|
56
|
+
CONSTRAINT "model_call_facts_model_bytes_check"
|
|
57
|
+
CHECK (octet_length("model") BETWEEN 1 AND 512)
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "model_call_facts_workspace_turn_source_uq"
|
|
61
|
+
ON "model_call_facts" ("workspace_id", "turn_id", "source_key");
|
|
62
|
+
|
|
63
|
+
CREATE INDEX IF NOT EXISTS "model_call_facts_workspace_occurred_idx"
|
|
64
|
+
ON "model_call_facts" ("workspace_id", "occurred_at");
|
|
65
|
+
|
|
66
|
+
CREATE INDEX IF NOT EXISTS "model_call_facts_workspace_provider_model_occurred_idx"
|
|
67
|
+
ON "model_call_facts" ("workspace_id", "provider", "model", "occurred_at");
|
|
68
|
+
|
|
69
|
+
CREATE INDEX IF NOT EXISTS "model_call_facts_workspace_session_occurred_idx"
|
|
70
|
+
ON "model_call_facts" ("workspace_id", "session_id", "occurred_at");
|
|
71
|
+
|
|
72
|
+
CREATE INDEX IF NOT EXISTS "model_call_facts_workspace_scheduled_task_occurred_idx"
|
|
73
|
+
ON "model_call_facts" ("workspace_id", "scheduled_task_id", "occurred_at")
|
|
74
|
+
WHERE "scheduled_task_id" IS NOT NULL;
|
|
75
|
+
|
|
76
|
+
ALTER TABLE "model_call_facts" ENABLE ROW LEVEL SECURITY;
|
|
77
|
+
ALTER TABLE "model_call_facts" FORCE ROW LEVEL SECURITY;
|
|
78
|
+
|
|
79
|
+
DROP POLICY IF EXISTS "model_call_facts_account_workspace_isolation" ON "model_call_facts";
|
|
80
|
+
CREATE POLICY "model_call_facts_account_workspace_isolation" ON "model_call_facts"
|
|
81
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
82
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
83
|
+
|
|
84
|
+
DO $grants$
|
|
85
|
+
DECLARE target_schema text := current_schema();
|
|
86
|
+
BEGIN
|
|
87
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
88
|
+
EXECUTE format(
|
|
89
|
+
'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.model_call_facts TO opengeni_app',
|
|
90
|
+
target_schema
|
|
91
|
+
);
|
|
92
|
+
END IF;
|
|
93
|
+
END $grants$;
|
|
94
|
+
|
|
95
|
+
RESET statement_timeout;
|
|
96
|
+
RESET lock_timeout;
|