@opengeni/db 3.6.0 → 3.7.4
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/canonical-human-identities.js +3 -3
- package/dist/{chunk-744DFC65.js → chunk-4NAR7FBA.js} +92 -6
- package/dist/chunk-4NAR7FBA.js.map +1 -0
- package/dist/{chunk-IW6O6G7R.js → chunk-5NNCGFQY.js} +2 -2
- package/dist/{chunk-G3732M3T.js → chunk-DZCYW2WT.js} +2 -2
- package/dist/{chunk-WW7AJ5IC.js → chunk-F6PBS6WE.js} +5 -7
- package/dist/chunk-F6PBS6WE.js.map +1 -0
- package/dist/{chunk-UJ2EW3QI.js → chunk-O56DM5GZ.js} +340 -85
- package/dist/chunk-O56DM5GZ.js.map +1 -0
- package/dist/{chunk-CDMMSUWJ.js → chunk-S3TDDCS2.js} +3 -3
- package/dist/{chunk-Q5YUGQVK.js → chunk-SZAWDXQC.js} +3 -3
- package/dist/{chunk-UGDVSQ6U.js → chunk-U4WPPEBH.js} +2 -2
- package/dist/{chunk-2NT476HD.js → chunk-US4IS6IV.js} +2 -2
- package/dist/codex-token-resolver.d.ts +12 -1
- package/dist/company-profile-agent-admin.d.ts +4 -2
- package/dist/company-profile-agent-policy.d.ts +20 -0
- package/dist/company-profile-schema.d.ts +573 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +382 -18
- package/dist/index.js +4513 -2065
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/managed-human-provisioning.d.ts +3 -3
- package/dist/memory-domain.d.ts +5 -3
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +5 -5
- package/dist/schema.d.ts +725 -4
- package/dist/schema.js +13 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-control.d.ts +40 -5
- package/dist/session-queue-commands.d.ts +2 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/user-resource-authority.d.ts +8 -0
- package/dist/video-generation.js +3 -3
- package/drizzle/0369_local_human_personal_authority.sql +350 -0
- package/drizzle/0370_workspace_member_management_scope.sql +98 -0
- package/drizzle/0371_workspace_member_candidate_inventory.sql +128 -0
- package/drizzle/0372_ordered_variable_set_runtime_authority.sql +287 -0
- package/drizzle/0373_sandbox_shared_preparation.sql +59 -0
- package/drizzle/0374_session_event_cursors.sql +315 -0
- package/drizzle/0375_session_recovery_observability.sql +73 -0
- package/drizzle/0376_organization_workspace_inventory.sql +88 -0
- package/drizzle/0377_scoped_rig_health_projection.sql +80 -0
- package/drizzle/0378_scoped_rig_health_audit_timestamp.sql +77 -0
- package/drizzle/0379_session_event_raw_lane_activation.sql +518 -0
- package/drizzle/0380_autonomous_company_profile_agent_policy.sql +829 -0
- package/drizzle/0381_organization_codex_subscription_inheritance.sql +720 -0
- package/drizzle/0382_organization_api_key_provenance.sql +91 -0
- package/drizzle/0383_codex_cooldown_reconciliation.sql +51 -0
- package/drizzle/0384_codex_cooldown_revision_guard_privileges.sql +6 -0
- package/drizzle/0385_connected_machine_legacy_tilde_workdirs.sql +25 -0
- package/drizzle/0386_local_organization_administration.sql +157 -0
- package/package.json +5 -5
- package/src/codex-token-resolver.ts +26 -1
- package/src/company-brain-governed-writes.ts +30 -1
- package/src/company-profile-agent-admin.ts +68 -1
- package/src/company-profile-agent-policy.ts +126 -0
- package/src/company-profile-schema.ts +115 -1
- package/src/database.ts +3 -5
- package/src/index.ts +3626 -456
- package/src/managed-human-provisioning.ts +3 -3
- package/src/memory-domain.ts +9 -4
- package/src/organization-membership-lifecycle.ts +0 -1
- package/src/provision-roles.ts +83 -4
- package/src/runtime-posture.ts +22 -0
- package/src/schema.ts +161 -8
- package/src/session-control.ts +177 -59
- package/src/session-queue-commands.ts +146 -40
- package/src/session-tool-call-settlement.ts +34 -7
- package/src/user-resource-authority.ts +56 -0
- package/dist/chunk-744DFC65.js.map +0 -1
- package/dist/chunk-UJ2EW3QI.js.map +0 -1
- package/dist/chunk-WW7AJ5IC.js.map +0 -1
- /package/dist/{chunk-IW6O6G7R.js.map → chunk-5NNCGFQY.js.map} +0 -0
- /package/dist/{chunk-G3732M3T.js.map → chunk-DZCYW2WT.js.map} +0 -0
- /package/dist/{chunk-CDMMSUWJ.js.map → chunk-S3TDDCS2.js.map} +0 -0
- /package/dist/{chunk-Q5YUGQVK.js.map → chunk-SZAWDXQC.js.map} +0 -0
- /package/dist/{chunk-UGDVSQ6U.js.map → chunk-U4WPPEBH.js.map} +0 -0
- /package/dist/{chunk-2NT476HD.js.map → chunk-US4IS6IV.js.map} +0 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Phase A of the narrow session-event sequencer: mirror and verify every
|
|
3
|
+
-- committed append before any writer stops using sessions.last_sequence.
|
|
4
|
+
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '10min';
|
|
7
|
+
|
|
8
|
+
CREATE TABLE session_event_cursors (
|
|
9
|
+
session_id uuid PRIMARY KEY REFERENCES sessions(id) ON DELETE CASCADE,
|
|
10
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
11
|
+
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
12
|
+
last_sequence integer NOT NULL DEFAULT 0,
|
|
13
|
+
revision bigint NOT NULL DEFAULT 0,
|
|
14
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
15
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
16
|
+
CONSTRAINT session_event_cursors_sequence_check CHECK (last_sequence >= 0),
|
|
17
|
+
CONSTRAINT session_event_cursors_revision_check CHECK (revision >= 0),
|
|
18
|
+
CONSTRAINT session_event_cursors_workspace_account_fk
|
|
19
|
+
FOREIGN KEY (workspace_id, account_id)
|
|
20
|
+
REFERENCES workspaces(id, account_id)
|
|
21
|
+
ON DELETE CASCADE,
|
|
22
|
+
CONSTRAINT session_event_cursors_workspace_session_fk
|
|
23
|
+
FOREIGN KEY (workspace_id, session_id)
|
|
24
|
+
REFERENCES sessions(workspace_id, id)
|
|
25
|
+
ON DELETE CASCADE
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
CREATE UNIQUE INDEX session_event_cursors_workspace_session_idx
|
|
29
|
+
ON session_event_cursors(workspace_id, session_id);
|
|
30
|
+
|
|
31
|
+
COMMENT ON TABLE session_event_cursors IS
|
|
32
|
+
'Narrow exact per-session event cursor. Migration 0374 mirrors sessions.last_sequence; a later activation may make this row the allocator and admission gate.';
|
|
33
|
+
COMMENT ON COLUMN session_event_cursors.revision IS
|
|
34
|
+
'Monotonic count of successful event-insert statements that advanced this cursor.';
|
|
35
|
+
|
|
36
|
+
ALTER TABLE session_event_cursors ENABLE ROW LEVEL SECURITY;
|
|
37
|
+
ALTER TABLE session_event_cursors FORCE ROW LEVEL SECURITY;
|
|
38
|
+
|
|
39
|
+
CREATE POLICY workspace_isolation ON session_event_cursors
|
|
40
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
41
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
42
|
+
|
|
43
|
+
-- The cursor is a direct session reference and must preserve private-session
|
|
44
|
+
-- visibility just like every other FORCE-RLS session-owned runtime table.
|
|
45
|
+
CREATE POLICY session_visibility_isolation ON session_event_cursors AS RESTRICTIVE
|
|
46
|
+
USING (session_reference_visible(account_id, workspace_id, session_id))
|
|
47
|
+
WITH CHECK (session_reference_visible(account_id, workspace_id, session_id));
|
|
48
|
+
|
|
49
|
+
DO $session_event_cursor_owner_policies$
|
|
50
|
+
DECLARE
|
|
51
|
+
target_schema text := pg_catalog.current_schema();
|
|
52
|
+
target_schema_oid oid := pg_catalog.current_schema()::pg_catalog.regnamespace;
|
|
53
|
+
migration_owner text := current_user;
|
|
54
|
+
BEGIN
|
|
55
|
+
EXECUTE pg_catalog.format(
|
|
56
|
+
'CREATE POLICY session_tenancy_fence_inventory_read ON %I.session_event_cursors '
|
|
57
|
+
|| 'FOR SELECT USING ('
|
|
58
|
+
|| '%I.session_tenancy_fence_owner_policy_active('
|
|
59
|
+
|| 'current_user, %L, %s::oid, workspace_id, true))',
|
|
60
|
+
target_schema,
|
|
61
|
+
target_schema,
|
|
62
|
+
migration_owner,
|
|
63
|
+
target_schema_oid
|
|
64
|
+
);
|
|
65
|
+
EXECUTE pg_catalog.format(
|
|
66
|
+
'CREATE POLICY session_tenancy_fenced_owner_write ON %I.session_event_cursors '
|
|
67
|
+
|| 'FOR ALL USING ('
|
|
68
|
+
|| '%I.session_tenancy_fence_owner_policy_active('
|
|
69
|
+
|| 'current_user, %L, %s::oid, workspace_id, false)) '
|
|
70
|
+
|| 'WITH CHECK ('
|
|
71
|
+
|| '%I.session_tenancy_fence_owner_policy_active('
|
|
72
|
+
|| 'current_user, %L, %s::oid, workspace_id, false))',
|
|
73
|
+
target_schema,
|
|
74
|
+
target_schema,
|
|
75
|
+
migration_owner,
|
|
76
|
+
target_schema_oid,
|
|
77
|
+
target_schema,
|
|
78
|
+
migration_owner,
|
|
79
|
+
target_schema_oid
|
|
80
|
+
);
|
|
81
|
+
END
|
|
82
|
+
$session_event_cursor_owner_policies$;
|
|
83
|
+
|
|
84
|
+
-- FORCE RLS remains enabled for runtime roles. Temporarily let the table owner
|
|
85
|
+
-- inspect all existing rows while the migration transaction holds the DDL
|
|
86
|
+
-- locks, then restore FORCE before commit.
|
|
87
|
+
ALTER TABLE sessions NO FORCE ROW LEVEL SECURITY;
|
|
88
|
+
ALTER TABLE session_events NO FORCE ROW LEVEL SECURITY;
|
|
89
|
+
ALTER TABLE session_event_cursors NO FORCE ROW LEVEL SECURITY;
|
|
90
|
+
|
|
91
|
+
DO $session_event_cursor_parity$
|
|
92
|
+
DECLARE
|
|
93
|
+
mismatch record;
|
|
94
|
+
BEGIN
|
|
95
|
+
SELECT
|
|
96
|
+
sessions.id AS session_id,
|
|
97
|
+
sessions.last_sequence AS stored_sequence,
|
|
98
|
+
COUNT(session_events.sequence)::integer AS event_count,
|
|
99
|
+
MIN(session_events.sequence)::integer AS first_event_sequence,
|
|
100
|
+
MAX(session_events.sequence)::integer AS last_event_sequence
|
|
101
|
+
INTO mismatch
|
|
102
|
+
FROM sessions
|
|
103
|
+
LEFT JOIN session_events
|
|
104
|
+
ON session_events.workspace_id = sessions.workspace_id
|
|
105
|
+
AND session_events.session_id = sessions.id
|
|
106
|
+
GROUP BY sessions.id, sessions.last_sequence
|
|
107
|
+
HAVING COUNT(session_events.sequence)::integer <> sessions.last_sequence
|
|
108
|
+
OR (
|
|
109
|
+
sessions.last_sequence = 0
|
|
110
|
+
AND (
|
|
111
|
+
MIN(session_events.sequence) IS NOT NULL
|
|
112
|
+
OR MAX(session_events.sequence) IS NOT NULL
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
OR (
|
|
116
|
+
sessions.last_sequence > 0
|
|
117
|
+
AND (
|
|
118
|
+
MIN(session_events.sequence)::integer <> 1
|
|
119
|
+
OR MAX(session_events.sequence)::integer <> sessions.last_sequence
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
LIMIT 1;
|
|
123
|
+
|
|
124
|
+
IF FOUND THEN
|
|
125
|
+
RAISE EXCEPTION USING
|
|
126
|
+
ERRCODE = '55000',
|
|
127
|
+
MESSAGE = 'session event cursor backfill refused because sessions.last_sequence diverges from durable event history',
|
|
128
|
+
DETAIL = pg_catalog.format(
|
|
129
|
+
'session_id=%s stored_sequence=%s event_count=%s first_event_sequence=%s last_event_sequence=%s',
|
|
130
|
+
mismatch.session_id,
|
|
131
|
+
mismatch.stored_sequence,
|
|
132
|
+
mismatch.event_count,
|
|
133
|
+
mismatch.first_event_sequence,
|
|
134
|
+
mismatch.last_event_sequence
|
|
135
|
+
);
|
|
136
|
+
END IF;
|
|
137
|
+
END
|
|
138
|
+
$session_event_cursor_parity$;
|
|
139
|
+
|
|
140
|
+
INSERT INTO session_event_cursors (
|
|
141
|
+
session_id,
|
|
142
|
+
account_id,
|
|
143
|
+
workspace_id,
|
|
144
|
+
last_sequence
|
|
145
|
+
)
|
|
146
|
+
SELECT id, account_id, workspace_id, last_sequence
|
|
147
|
+
FROM sessions;
|
|
148
|
+
|
|
149
|
+
ALTER TABLE session_event_cursors FORCE ROW LEVEL SECURITY;
|
|
150
|
+
ALTER TABLE session_events FORCE ROW LEVEL SECURITY;
|
|
151
|
+
ALTER TABLE sessions FORCE ROW LEVEL SECURITY;
|
|
152
|
+
|
|
153
|
+
CREATE FUNCTION initialize_session_event_cursors_for_inserted_sessions()
|
|
154
|
+
RETURNS trigger
|
|
155
|
+
LANGUAGE plpgsql
|
|
156
|
+
SET search_path FROM CURRENT
|
|
157
|
+
AS $initialize_session_event_cursors$
|
|
158
|
+
BEGIN
|
|
159
|
+
INSERT INTO session_event_cursors (
|
|
160
|
+
session_id,
|
|
161
|
+
account_id,
|
|
162
|
+
workspace_id,
|
|
163
|
+
last_sequence
|
|
164
|
+
)
|
|
165
|
+
-- A session row must exist before any event can reference it, so a newly
|
|
166
|
+
-- inserted session has no durable event history yet. Some lifecycle
|
|
167
|
+
-- functions pre-seed sessions.last_sequence for events they insert later in
|
|
168
|
+
-- the same transaction; copying that future projection here would make the
|
|
169
|
+
-- first real event look like a duplicate.
|
|
170
|
+
SELECT id, account_id, workspace_id, 0
|
|
171
|
+
FROM inserted_sessions
|
|
172
|
+
ON CONFLICT (session_id) DO NOTHING;
|
|
173
|
+
RETURN NULL;
|
|
174
|
+
END
|
|
175
|
+
$initialize_session_event_cursors$;
|
|
176
|
+
|
|
177
|
+
CREATE TRIGGER sessions_initialize_event_cursors
|
|
178
|
+
AFTER INSERT ON sessions
|
|
179
|
+
REFERENCING NEW TABLE AS inserted_sessions
|
|
180
|
+
FOR EACH STATEMENT
|
|
181
|
+
EXECUTE FUNCTION initialize_session_event_cursors_for_inserted_sessions();
|
|
182
|
+
|
|
183
|
+
CREATE FUNCTION advance_session_event_cursors_for_inserted_events()
|
|
184
|
+
RETURNS trigger
|
|
185
|
+
LANGUAGE plpgsql
|
|
186
|
+
SECURITY DEFINER
|
|
187
|
+
SET search_path FROM CURRENT
|
|
188
|
+
AS $advance_session_event_cursors$
|
|
189
|
+
DECLARE
|
|
190
|
+
fenced_access_capability_id uuid;
|
|
191
|
+
inserted_group record;
|
|
192
|
+
current_sequence integer;
|
|
193
|
+
BEGIN
|
|
194
|
+
-- Runtime writers already hold the exact workspace tenancy fence before an
|
|
195
|
+
-- event insert. Open only the transaction-local owner capability needed for
|
|
196
|
+
-- this trigger to inspect and advance the FORCE-RLS cursor under that fence.
|
|
197
|
+
fenced_access_capability_id :=
|
|
198
|
+
opengeni_private.open_session_tenancy_fenced_access(
|
|
199
|
+
session_tenancy_fence_target_schema()
|
|
200
|
+
);
|
|
201
|
+
FOR inserted_group IN
|
|
202
|
+
SELECT
|
|
203
|
+
account_id,
|
|
204
|
+
workspace_id,
|
|
205
|
+
session_id,
|
|
206
|
+
MIN(sequence)::integer AS first_sequence,
|
|
207
|
+
MAX(sequence)::integer AS last_sequence,
|
|
208
|
+
COUNT(*)::integer AS sequence_count,
|
|
209
|
+
COUNT(DISTINCT sequence)::integer AS distinct_sequence_count
|
|
210
|
+
FROM inserted_session_events
|
|
211
|
+
GROUP BY account_id, workspace_id, session_id
|
|
212
|
+
ORDER BY workspace_id, session_id
|
|
213
|
+
LOOP
|
|
214
|
+
SELECT last_sequence
|
|
215
|
+
INTO current_sequence
|
|
216
|
+
FROM session_event_cursors
|
|
217
|
+
WHERE account_id = inserted_group.account_id
|
|
218
|
+
AND workspace_id = inserted_group.workspace_id
|
|
219
|
+
AND session_id = inserted_group.session_id
|
|
220
|
+
FOR UPDATE;
|
|
221
|
+
|
|
222
|
+
IF NOT FOUND THEN
|
|
223
|
+
RAISE EXCEPTION USING
|
|
224
|
+
ERRCODE = '55000',
|
|
225
|
+
MESSAGE = 'session event cursor is missing for an inserted event',
|
|
226
|
+
DETAIL = pg_catalog.format(
|
|
227
|
+
'workspace_id=%s session_id=%s',
|
|
228
|
+
inserted_group.workspace_id,
|
|
229
|
+
inserted_group.session_id
|
|
230
|
+
);
|
|
231
|
+
END IF;
|
|
232
|
+
|
|
233
|
+
IF inserted_group.distinct_sequence_count <> inserted_group.sequence_count
|
|
234
|
+
OR inserted_group.first_sequence <> current_sequence + 1
|
|
235
|
+
OR inserted_group.last_sequence <> current_sequence + inserted_group.sequence_count
|
|
236
|
+
THEN
|
|
237
|
+
RAISE EXCEPTION USING
|
|
238
|
+
ERRCODE = '55000',
|
|
239
|
+
MESSAGE = 'session event insert does not exactly continue the durable cursor',
|
|
240
|
+
DETAIL = pg_catalog.format(
|
|
241
|
+
'workspace_id=%s session_id=%s current=%s first=%s last=%s count=%s distinct=%s',
|
|
242
|
+
inserted_group.workspace_id,
|
|
243
|
+
inserted_group.session_id,
|
|
244
|
+
current_sequence,
|
|
245
|
+
inserted_group.first_sequence,
|
|
246
|
+
inserted_group.last_sequence,
|
|
247
|
+
inserted_group.sequence_count,
|
|
248
|
+
inserted_group.distinct_sequence_count
|
|
249
|
+
);
|
|
250
|
+
END IF;
|
|
251
|
+
|
|
252
|
+
UPDATE session_event_cursors
|
|
253
|
+
SET last_sequence = inserted_group.last_sequence,
|
|
254
|
+
revision = revision + 1,
|
|
255
|
+
updated_at = now()
|
|
256
|
+
WHERE account_id = inserted_group.account_id
|
|
257
|
+
AND workspace_id = inserted_group.workspace_id
|
|
258
|
+
AND session_id = inserted_group.session_id
|
|
259
|
+
AND last_sequence = current_sequence;
|
|
260
|
+
|
|
261
|
+
IF NOT FOUND THEN
|
|
262
|
+
RAISE EXCEPTION USING
|
|
263
|
+
ERRCODE = '40001',
|
|
264
|
+
MESSAGE = 'session event cursor changed while applying an inserted event statement';
|
|
265
|
+
END IF;
|
|
266
|
+
END LOOP;
|
|
267
|
+
PERFORM opengeni_private.close_session_tenancy_fenced_access(
|
|
268
|
+
fenced_access_capability_id
|
|
269
|
+
);
|
|
270
|
+
RETURN NULL;
|
|
271
|
+
EXCEPTION WHEN OTHERS THEN
|
|
272
|
+
PERFORM opengeni_private.close_session_tenancy_fenced_access(
|
|
273
|
+
fenced_access_capability_id
|
|
274
|
+
);
|
|
275
|
+
RAISE;
|
|
276
|
+
END
|
|
277
|
+
$advance_session_event_cursors$;
|
|
278
|
+
|
|
279
|
+
-- Trigger functions may resolve target-schema relations, but pg_temp must not
|
|
280
|
+
-- precede those relations. In particular, the SECURITY DEFINER event trigger
|
|
281
|
+
-- must not be redirected through caller-owned temporary objects.
|
|
282
|
+
DO $session_event_cursor_function_paths$
|
|
283
|
+
DECLARE
|
|
284
|
+
target_schema text := pg_catalog.current_schema();
|
|
285
|
+
BEGIN
|
|
286
|
+
EXECUTE pg_catalog.format(
|
|
287
|
+
'ALTER FUNCTION %I.initialize_session_event_cursors_for_inserted_sessions() '
|
|
288
|
+
|| 'SET search_path = pg_catalog, %I, pg_temp',
|
|
289
|
+
target_schema,
|
|
290
|
+
target_schema
|
|
291
|
+
);
|
|
292
|
+
EXECUTE pg_catalog.format(
|
|
293
|
+
'ALTER FUNCTION %I.advance_session_event_cursors_for_inserted_events() '
|
|
294
|
+
|| 'SET search_path = pg_catalog, %I, pg_temp',
|
|
295
|
+
target_schema,
|
|
296
|
+
target_schema
|
|
297
|
+
);
|
|
298
|
+
END
|
|
299
|
+
$session_event_cursor_function_paths$;
|
|
300
|
+
|
|
301
|
+
REVOKE ALL ON FUNCTION advance_session_event_cursors_for_inserted_events() FROM PUBLIC;
|
|
302
|
+
|
|
303
|
+
CREATE TRIGGER session_events_advance_event_cursors
|
|
304
|
+
AFTER INSERT ON session_events
|
|
305
|
+
REFERENCING NEW TABLE AS inserted_session_events
|
|
306
|
+
FOR EACH STATEMENT
|
|
307
|
+
EXECUTE FUNCTION advance_session_event_cursors_for_inserted_events();
|
|
308
|
+
|
|
309
|
+
DO $session_event_cursor_runtime_grants$
|
|
310
|
+
BEGIN
|
|
311
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
312
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON session_event_cursors TO opengeni_app;
|
|
313
|
+
END IF;
|
|
314
|
+
END
|
|
315
|
+
$session_event_cursor_runtime_grants$;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Expose one content-free, bounded-cardinality aggregate for durable recovery
|
|
3
|
+
-- states that process-local turn gauges cannot see.
|
|
4
|
+
|
|
5
|
+
CREATE OR REPLACE FUNCTION opengeni_private.count_session_recovery_backlog()
|
|
6
|
+
RETURNS TABLE (state text, count bigint)
|
|
7
|
+
LANGUAGE sql
|
|
8
|
+
STABLE
|
|
9
|
+
SECURITY DEFINER
|
|
10
|
+
SET search_path FROM CURRENT
|
|
11
|
+
AS $count_session_recovery_backlog$
|
|
12
|
+
WITH recovery_states(state) AS (
|
|
13
|
+
VALUES ('quiescence_missing'::text), ('projection_stale'::text)
|
|
14
|
+
), candidates AS (
|
|
15
|
+
SELECT CASE
|
|
16
|
+
WHEN attempt.quiesced_at IS NULL THEN 'quiescence_missing'::text
|
|
17
|
+
ELSE 'projection_stale'::text
|
|
18
|
+
END AS state
|
|
19
|
+
FROM sessions session
|
|
20
|
+
JOIN session_turns turn
|
|
21
|
+
ON turn.workspace_id = session.workspace_id
|
|
22
|
+
AND turn.session_id = session.id
|
|
23
|
+
AND turn.id = session.active_turn_id
|
|
24
|
+
JOIN LATERAL (
|
|
25
|
+
SELECT candidate.id, candidate.state, candidate.outcome, candidate.quiesced_at
|
|
26
|
+
FROM session_turn_attempts candidate
|
|
27
|
+
WHERE candidate.workspace_id = turn.workspace_id
|
|
28
|
+
AND candidate.session_id = turn.session_id
|
|
29
|
+
AND candidate.turn_id = turn.id
|
|
30
|
+
ORDER BY candidate.execution_generation DESC, candidate.updated_at DESC, candidate.id DESC
|
|
31
|
+
LIMIT 1
|
|
32
|
+
) attempt ON true
|
|
33
|
+
WHERE session.status = 'recovering'
|
|
34
|
+
AND turn.status = 'recovering'
|
|
35
|
+
AND turn.active_attempt_id IS NULL
|
|
36
|
+
AND attempt.state = 'closed'
|
|
37
|
+
AND attempt.outcome = 'interrupted_recoverable'
|
|
38
|
+
AND (
|
|
39
|
+
EXISTS (
|
|
40
|
+
SELECT 1
|
|
41
|
+
FROM session_attempt_interruptions interruption
|
|
42
|
+
WHERE interruption.workspace_id = session.workspace_id
|
|
43
|
+
AND interruption.session_id = session.id
|
|
44
|
+
AND interruption.attempt_id = attempt.id
|
|
45
|
+
AND interruption.state IN ('settled', 'rejected_stale')
|
|
46
|
+
)
|
|
47
|
+
OR EXISTS (
|
|
48
|
+
SELECT 1
|
|
49
|
+
FROM session_events event
|
|
50
|
+
WHERE event.workspace_id = session.workspace_id
|
|
51
|
+
AND event.session_id = session.id
|
|
52
|
+
AND event.turn_id = turn.id
|
|
53
|
+
AND event.turn_attempt_id = attempt.id
|
|
54
|
+
AND event.type = 'turn.recovery.requested'
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
SELECT recovery_states.state, count(candidates.state)::bigint
|
|
59
|
+
FROM recovery_states
|
|
60
|
+
LEFT JOIN candidates USING (state)
|
|
61
|
+
GROUP BY recovery_states.state
|
|
62
|
+
ORDER BY recovery_states.state;
|
|
63
|
+
$count_session_recovery_backlog$;
|
|
64
|
+
|
|
65
|
+
REVOKE ALL ON FUNCTION opengeni_private.count_session_recovery_backlog() FROM PUBLIC;
|
|
66
|
+
|
|
67
|
+
DO $session_recovery_observability_grants$
|
|
68
|
+
BEGIN
|
|
69
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
70
|
+
GRANT EXECUTE ON FUNCTION opengeni_private.count_session_recovery_backlog() TO opengeni_app;
|
|
71
|
+
END IF;
|
|
72
|
+
END
|
|
73
|
+
$session_recovery_observability_grants$;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Account-scoped organization API keys need one complete shared-workspace
|
|
3
|
+
-- inventory without direct runtime access to organization membership rows.
|
|
4
|
+
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '5min';
|
|
7
|
+
|
|
8
|
+
CREATE FUNCTION list_organization_workspace_ids(p_account_id uuid)
|
|
9
|
+
RETURNS TABLE (workspace_id uuid)
|
|
10
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
11
|
+
SET search_path FROM CURRENT
|
|
12
|
+
AS $body$
|
|
13
|
+
DECLARE
|
|
14
|
+
previous_lifecycle text := current_setting('opengeni.organization_tenancy_lifecycle', true);
|
|
15
|
+
BEGIN
|
|
16
|
+
IF p_account_id IS NULL
|
|
17
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
18
|
+
OR opengeni_private.current_workspace_id() IS NOT NULL
|
|
19
|
+
THEN
|
|
20
|
+
RAISE EXCEPTION 'organization workspace inventory authority required'
|
|
21
|
+
USING ERRCODE = '42501';
|
|
22
|
+
END IF;
|
|
23
|
+
|
|
24
|
+
PERFORM pg_catalog.set_config(
|
|
25
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
26
|
+
'organization_membership_lifecycle', true
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
RETURN QUERY
|
|
30
|
+
SELECT workspace.id
|
|
31
|
+
FROM workspaces workspace
|
|
32
|
+
WHERE workspace.account_id = p_account_id
|
|
33
|
+
AND NOT EXISTS (
|
|
34
|
+
SELECT 1
|
|
35
|
+
FROM organization_memberships membership
|
|
36
|
+
WHERE membership.account_id = p_account_id
|
|
37
|
+
AND membership.personal_workspace_id = workspace.id
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
PERFORM pg_catalog.set_config(
|
|
41
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
42
|
+
);
|
|
43
|
+
RETURN;
|
|
44
|
+
EXCEPTION WHEN OTHERS THEN
|
|
45
|
+
PERFORM pg_catalog.set_config(
|
|
46
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
47
|
+
);
|
|
48
|
+
RAISE;
|
|
49
|
+
END
|
|
50
|
+
$body$;
|
|
51
|
+
|
|
52
|
+
REVOKE ALL ON FUNCTION list_organization_workspace_ids(uuid) FROM PUBLIC;
|
|
53
|
+
|
|
54
|
+
DO $pin_and_grant_organization_workspace_inventory$
|
|
55
|
+
DECLARE
|
|
56
|
+
data_schema text := current_schema();
|
|
57
|
+
application_role text;
|
|
58
|
+
BEGIN
|
|
59
|
+
EXECUTE format(
|
|
60
|
+
'ALTER FUNCTION %I.list_organization_workspace_ids(uuid) '
|
|
61
|
+
'SET search_path = pg_catalog, %I, pg_temp',
|
|
62
|
+
data_schema, data_schema
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
FOR application_role IN
|
|
66
|
+
SELECT role_value.rolname
|
|
67
|
+
FROM pg_catalog.jsonb_array_elements_text(
|
|
68
|
+
coalesce(
|
|
69
|
+
nullif(current_setting('opengeni.migration_application_roles', true), ''),
|
|
70
|
+
'[]'
|
|
71
|
+
)::jsonb
|
|
72
|
+
) configured(value)
|
|
73
|
+
JOIN pg_catalog.pg_roles role_value
|
|
74
|
+
ON role_value.rolname = configured.value
|
|
75
|
+
UNION
|
|
76
|
+
SELECT 'opengeni_app'
|
|
77
|
+
WHERE EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = 'opengeni_app')
|
|
78
|
+
LOOP
|
|
79
|
+
EXECUTE format(
|
|
80
|
+
'GRANT EXECUTE ON FUNCTION %I.list_organization_workspace_ids(uuid) TO %I',
|
|
81
|
+
data_schema, application_role
|
|
82
|
+
);
|
|
83
|
+
END LOOP;
|
|
84
|
+
END
|
|
85
|
+
$pin_and_grant_organization_workspace_inventory$;
|
|
86
|
+
|
|
87
|
+
COMMENT ON FUNCTION list_organization_workspace_ids(uuid) IS
|
|
88
|
+
'Complete content-free organization workspace inventory. Canonical personal-workspace pointers are excluded under exact account scope.';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Keep the access-controlled Rig projection in parity with the ordinary
|
|
3
|
+
-- workspace query: terminal verification evidence for the active version must
|
|
4
|
+
-- be visible to REST and first-party MCP callers instead of staying unknown.
|
|
5
|
+
|
|
6
|
+
SET LOCAL lock_timeout = '5s';
|
|
7
|
+
SET LOCAL statement_timeout = '5min';
|
|
8
|
+
|
|
9
|
+
CREATE OR REPLACE FUNCTION scoped_rig_json(p_rig_id uuid) RETURNS jsonb
|
|
10
|
+
LANGUAGE sql STABLE SECURITY DEFINER SET search_path FROM CURRENT AS $$
|
|
11
|
+
SELECT pg_catalog.jsonb_build_object(
|
|
12
|
+
'id', rig.id, 'accountId', rig.account_id, 'workspaceId', rig.workspace_id,
|
|
13
|
+
'scope', rig.authority_scope, 'generation', rig.generation, 'status', rig.status,
|
|
14
|
+
'name', rig.name, 'description', rig.description, 'createdBy', rig.created_by,
|
|
15
|
+
'activeVersion', (
|
|
16
|
+
SELECT pg_catalog.jsonb_build_object(
|
|
17
|
+
'id', version.id, 'rigId', version.rig_id, 'version', version.version,
|
|
18
|
+
'image', version.image, 'setupScript', version.setup_script,
|
|
19
|
+
'checks', version.checks, 'credentialHooks', version.credential_hooks,
|
|
20
|
+
'defaultVariableSetIds', version.default_variable_set_ids,
|
|
21
|
+
'changelog', version.changelog, 'providerImages', version.provider_images,
|
|
22
|
+
'createdBy', version.created_by, 'active', version.active,
|
|
23
|
+
'createdAt', version.created_at
|
|
24
|
+
) FROM rig_versions version
|
|
25
|
+
WHERE version.rig_id = rig.id AND version.account_id = rig.account_id
|
|
26
|
+
AND version.active = true LIMIT 1
|
|
27
|
+
),
|
|
28
|
+
'activeVersionHealth', (
|
|
29
|
+
SELECT pg_catalog.jsonb_build_object(
|
|
30
|
+
'checkHealth', coalesce(health.check_health, 'unknown'),
|
|
31
|
+
'lastVerifiedAt', health.verified_at
|
|
32
|
+
)
|
|
33
|
+
FROM rig_versions active_version
|
|
34
|
+
LEFT JOIN LATERAL (
|
|
35
|
+
SELECT candidate.check_health, candidate.verified_at
|
|
36
|
+
FROM (
|
|
37
|
+
SELECT CASE change.verification ->> 'passed'
|
|
38
|
+
WHEN 'true' THEN 'passing' WHEN 'false' THEN 'failing' END AS check_health,
|
|
39
|
+
coalesce(
|
|
40
|
+
nullif(change.verification ->> 'finishedAt', '')::timestamptz,
|
|
41
|
+
change.updated_at
|
|
42
|
+
) AS verified_at
|
|
43
|
+
FROM rig_changes change
|
|
44
|
+
WHERE change.account_id = active_version.account_id
|
|
45
|
+
AND change.workspace_id = active_version.workspace_id
|
|
46
|
+
AND change.result_version_id = active_version.id
|
|
47
|
+
AND change.verification ->> 'passed' IN ('true', 'false')
|
|
48
|
+
UNION ALL
|
|
49
|
+
SELECT CASE event.action WHEN 'rig.verification.passed' THEN 'passing'
|
|
50
|
+
ELSE 'failing' END,
|
|
51
|
+
coalesce(
|
|
52
|
+
nullif(event.metadata ->> 'finishedAt', '')::timestamptz,
|
|
53
|
+
event.occurred_at
|
|
54
|
+
)
|
|
55
|
+
FROM audit_events event
|
|
56
|
+
WHERE event.account_id = active_version.account_id
|
|
57
|
+
AND event.workspace_id = active_version.workspace_id
|
|
58
|
+
AND event.target_type = 'rig'
|
|
59
|
+
AND event.target_id = rig.id::text
|
|
60
|
+
AND event.action IN ('rig.verification.passed', 'rig.verification.failed')
|
|
61
|
+
AND event.metadata ->> 'versionId' = active_version.id::text
|
|
62
|
+
) candidate
|
|
63
|
+
ORDER BY candidate.verified_at DESC
|
|
64
|
+
LIMIT 1
|
|
65
|
+
) health ON true
|
|
66
|
+
WHERE active_version.rig_id = rig.id
|
|
67
|
+
AND active_version.account_id = rig.account_id
|
|
68
|
+
AND active_version.active = true
|
|
69
|
+
LIMIT 1
|
|
70
|
+
),
|
|
71
|
+
'versionCount', (SELECT count(*)::integer FROM rig_versions version
|
|
72
|
+
WHERE version.rig_id = rig.id AND version.account_id = rig.account_id),
|
|
73
|
+
'createdAt', rig.created_at, 'updatedAt', rig.updated_at
|
|
74
|
+
) FROM rigs rig WHERE rig.id = p_rig_id
|
|
75
|
+
$$;
|
|
76
|
+
|
|
77
|
+
REVOKE ALL ON FUNCTION scoped_rig_json(uuid) FROM PUBLIC;
|
|
78
|
+
|
|
79
|
+
COMMENT ON FUNCTION scoped_rig_json(uuid) IS
|
|
80
|
+
'Access-controlled Rig projection with active-version health from the latest terminal change or audit verification evidence.';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Repair the already-ledgered scoped Rig health projection so generic audit
|
|
3
|
+
-- metadata can never abort a SECURITY DEFINER read. Audit occurrence time is
|
|
4
|
+
-- the trusted ordering and display timestamp for audit-backed evidence.
|
|
5
|
+
|
|
6
|
+
SET LOCAL lock_timeout = '5s';
|
|
7
|
+
SET LOCAL statement_timeout = '5min';
|
|
8
|
+
|
|
9
|
+
CREATE OR REPLACE FUNCTION scoped_rig_json(p_rig_id uuid) RETURNS jsonb
|
|
10
|
+
LANGUAGE sql STABLE SECURITY DEFINER SET search_path FROM CURRENT AS $$
|
|
11
|
+
SELECT pg_catalog.jsonb_build_object(
|
|
12
|
+
'id', rig.id, 'accountId', rig.account_id, 'workspaceId', rig.workspace_id,
|
|
13
|
+
'scope', rig.authority_scope, 'generation', rig.generation, 'status', rig.status,
|
|
14
|
+
'name', rig.name, 'description', rig.description, 'createdBy', rig.created_by,
|
|
15
|
+
'activeVersion', (
|
|
16
|
+
SELECT pg_catalog.jsonb_build_object(
|
|
17
|
+
'id', version.id, 'rigId', version.rig_id, 'version', version.version,
|
|
18
|
+
'image', version.image, 'setupScript', version.setup_script,
|
|
19
|
+
'checks', version.checks, 'credentialHooks', version.credential_hooks,
|
|
20
|
+
'defaultVariableSetIds', version.default_variable_set_ids,
|
|
21
|
+
'changelog', version.changelog, 'providerImages', version.provider_images,
|
|
22
|
+
'createdBy', version.created_by, 'active', version.active,
|
|
23
|
+
'createdAt', version.created_at
|
|
24
|
+
) FROM rig_versions version
|
|
25
|
+
WHERE version.rig_id = rig.id AND version.account_id = rig.account_id
|
|
26
|
+
AND version.active = true LIMIT 1
|
|
27
|
+
),
|
|
28
|
+
'activeVersionHealth', (
|
|
29
|
+
SELECT pg_catalog.jsonb_build_object(
|
|
30
|
+
'checkHealth', coalesce(health.check_health, 'unknown'),
|
|
31
|
+
'lastVerifiedAt', health.verified_at
|
|
32
|
+
)
|
|
33
|
+
FROM rig_versions active_version
|
|
34
|
+
LEFT JOIN LATERAL (
|
|
35
|
+
SELECT candidate.check_health, candidate.verified_at
|
|
36
|
+
FROM (
|
|
37
|
+
SELECT CASE change.verification ->> 'passed'
|
|
38
|
+
WHEN 'true' THEN 'passing' WHEN 'false' THEN 'failing' END AS check_health,
|
|
39
|
+
coalesce(
|
|
40
|
+
nullif(change.verification ->> 'finishedAt', '')::timestamptz,
|
|
41
|
+
change.updated_at
|
|
42
|
+
) AS verified_at
|
|
43
|
+
FROM rig_changes change
|
|
44
|
+
WHERE change.account_id = active_version.account_id
|
|
45
|
+
AND change.workspace_id = active_version.workspace_id
|
|
46
|
+
AND change.result_version_id = active_version.id
|
|
47
|
+
AND change.verification ->> 'passed' IN ('true', 'false')
|
|
48
|
+
UNION ALL
|
|
49
|
+
SELECT CASE event.action WHEN 'rig.verification.passed' THEN 'passing'
|
|
50
|
+
ELSE 'failing' END,
|
|
51
|
+
event.occurred_at
|
|
52
|
+
FROM audit_events event
|
|
53
|
+
WHERE event.account_id = active_version.account_id
|
|
54
|
+
AND event.workspace_id = active_version.workspace_id
|
|
55
|
+
AND event.target_type = 'rig'
|
|
56
|
+
AND event.target_id = rig.id::text
|
|
57
|
+
AND event.action IN ('rig.verification.passed', 'rig.verification.failed')
|
|
58
|
+
AND event.metadata ->> 'versionId' = active_version.id::text
|
|
59
|
+
) candidate
|
|
60
|
+
ORDER BY candidate.verified_at DESC
|
|
61
|
+
LIMIT 1
|
|
62
|
+
) health ON true
|
|
63
|
+
WHERE active_version.rig_id = rig.id
|
|
64
|
+
AND active_version.account_id = rig.account_id
|
|
65
|
+
AND active_version.active = true
|
|
66
|
+
LIMIT 1
|
|
67
|
+
),
|
|
68
|
+
'versionCount', (SELECT count(*)::integer FROM rig_versions version
|
|
69
|
+
WHERE version.rig_id = rig.id AND version.account_id = rig.account_id),
|
|
70
|
+
'createdAt', rig.created_at, 'updatedAt', rig.updated_at
|
|
71
|
+
) FROM rigs rig WHERE rig.id = p_rig_id
|
|
72
|
+
$$;
|
|
73
|
+
|
|
74
|
+
REVOKE ALL ON FUNCTION scoped_rig_json(uuid) FROM PUBLIC;
|
|
75
|
+
|
|
76
|
+
COMMENT ON FUNCTION scoped_rig_json(uuid) IS
|
|
77
|
+
'Access-controlled Rig projection with active-version health from terminal evidence and trusted audit occurrence timestamps.';
|