@opengeni/db 3.6.0 → 3.7.3-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/{chunk-744DFC65.js → chunk-4NAR7FBA.js} +92 -6
  3. package/dist/chunk-4NAR7FBA.js.map +1 -0
  4. package/dist/{chunk-IW6O6G7R.js → chunk-5NNCGFQY.js} +2 -2
  5. package/dist/{chunk-G3732M3T.js → chunk-DZCYW2WT.js} +2 -2
  6. package/dist/{chunk-WW7AJ5IC.js → chunk-F6PBS6WE.js} +5 -7
  7. package/dist/chunk-F6PBS6WE.js.map +1 -0
  8. package/dist/{chunk-UJ2EW3QI.js → chunk-O56DM5GZ.js} +340 -85
  9. package/dist/chunk-O56DM5GZ.js.map +1 -0
  10. package/dist/{chunk-CDMMSUWJ.js → chunk-S3TDDCS2.js} +3 -3
  11. package/dist/{chunk-Q5YUGQVK.js → chunk-SZAWDXQC.js} +3 -3
  12. package/dist/{chunk-UGDVSQ6U.js → chunk-U4WPPEBH.js} +2 -2
  13. package/dist/{chunk-2NT476HD.js → chunk-US4IS6IV.js} +2 -2
  14. package/dist/codex-token-resolver.d.ts +12 -1
  15. package/dist/company-profile-agent-admin.d.ts +4 -2
  16. package/dist/company-profile-agent-policy.d.ts +20 -0
  17. package/dist/company-profile-schema.d.ts +573 -0
  18. package/dist/editable-artifact-durable-export.js +2 -2
  19. package/dist/editable-artifacts.js +3 -3
  20. package/dist/index.d.ts +382 -18
  21. package/dist/index.js +4513 -2065
  22. package/dist/index.js.map +1 -1
  23. package/dist/managed-auth-session-sets.js +3 -3
  24. package/dist/managed-human-provisioning.d.ts +3 -3
  25. package/dist/memory-domain.d.ts +5 -3
  26. package/dist/provision-roles.js +1 -1
  27. package/dist/runtime-posture.d.ts +5 -5
  28. package/dist/schema.d.ts +725 -4
  29. package/dist/schema.js +13 -1
  30. package/dist/session-background-commands.js +3 -3
  31. package/dist/session-control.d.ts +40 -5
  32. package/dist/session-queue-commands.d.ts +2 -0
  33. package/dist/session-tenancy.js +3 -3
  34. package/dist/user-resource-authority.d.ts +8 -0
  35. package/dist/video-generation.js +3 -3
  36. package/drizzle/0369_local_human_personal_authority.sql +350 -0
  37. package/drizzle/0370_workspace_member_management_scope.sql +98 -0
  38. package/drizzle/0371_workspace_member_candidate_inventory.sql +128 -0
  39. package/drizzle/0372_ordered_variable_set_runtime_authority.sql +287 -0
  40. package/drizzle/0373_sandbox_shared_preparation.sql +59 -0
  41. package/drizzle/0374_session_event_cursors.sql +315 -0
  42. package/drizzle/0375_session_recovery_observability.sql +73 -0
  43. package/drizzle/0376_organization_workspace_inventory.sql +88 -0
  44. package/drizzle/0377_scoped_rig_health_projection.sql +80 -0
  45. package/drizzle/0378_scoped_rig_health_audit_timestamp.sql +77 -0
  46. package/drizzle/0379_session_event_raw_lane_activation.sql +518 -0
  47. package/drizzle/0380_autonomous_company_profile_agent_policy.sql +829 -0
  48. package/drizzle/0381_organization_codex_subscription_inheritance.sql +720 -0
  49. package/drizzle/0382_organization_api_key_provenance.sql +91 -0
  50. package/drizzle/0383_codex_cooldown_reconciliation.sql +51 -0
  51. package/drizzle/0384_codex_cooldown_revision_guard_privileges.sql +6 -0
  52. package/drizzle/0385_connected_machine_legacy_tilde_workdirs.sql +25 -0
  53. package/drizzle/0386_local_organization_administration.sql +157 -0
  54. package/package.json +6 -6
  55. package/src/codex-token-resolver.ts +26 -1
  56. package/src/company-brain-governed-writes.ts +30 -1
  57. package/src/company-profile-agent-admin.ts +68 -1
  58. package/src/company-profile-agent-policy.ts +126 -0
  59. package/src/company-profile-schema.ts +115 -1
  60. package/src/database.ts +3 -5
  61. package/src/index.ts +3626 -456
  62. package/src/managed-human-provisioning.ts +3 -3
  63. package/src/memory-domain.ts +9 -4
  64. package/src/organization-membership-lifecycle.ts +0 -1
  65. package/src/provision-roles.ts +83 -4
  66. package/src/runtime-posture.ts +22 -0
  67. package/src/schema.ts +161 -8
  68. package/src/session-control.ts +177 -59
  69. package/src/session-queue-commands.ts +146 -40
  70. package/src/session-tool-call-settlement.ts +34 -7
  71. package/src/user-resource-authority.ts +56 -0
  72. package/dist/chunk-744DFC65.js.map +0 -1
  73. package/dist/chunk-UJ2EW3QI.js.map +0 -1
  74. package/dist/chunk-WW7AJ5IC.js.map +0 -1
  75. /package/dist/{chunk-IW6O6G7R.js.map → chunk-5NNCGFQY.js.map} +0 -0
  76. /package/dist/{chunk-G3732M3T.js.map → chunk-DZCYW2WT.js.map} +0 -0
  77. /package/dist/{chunk-CDMMSUWJ.js.map → chunk-S3TDDCS2.js.map} +0 -0
  78. /package/dist/{chunk-Q5YUGQVK.js.map → chunk-SZAWDXQC.js.map} +0 -0
  79. /package/dist/{chunk-UGDVSQ6U.js.map → chunk-U4WPPEBH.js.map} +0 -0
  80. /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.';