@opengeni/db 3.7.4 → 3.8.2-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.
- package/dist/automations.d.ts +4 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/{chunk-O56DM5GZ.js → chunk-53A2PEHT.js} +328 -31
- package/dist/chunk-53A2PEHT.js.map +1 -0
- package/dist/{chunk-SZAWDXQC.js → chunk-5S4BQAHX.js} +3 -3
- package/dist/{chunk-S3TDDCS2.js → chunk-CITYUGMW.js} +3 -3
- package/dist/{chunk-4NAR7FBA.js → chunk-F5YOZEYQ.js} +49 -1
- package/dist/chunk-F5YOZEYQ.js.map +1 -0
- package/dist/{chunk-F6PBS6WE.js → chunk-HZV7O6JR.js} +2 -2
- package/dist/{chunk-5NNCGFQY.js → chunk-RLQVPOVR.js} +2 -2
- package/dist/{chunk-DZCYW2WT.js → chunk-UKOGHUGC.js} +2 -2
- package/dist/{chunk-US4IS6IV.js → chunk-X6TO5HII.js} +2 -2
- package/dist/{chunk-U4WPPEBH.js → chunk-Y53WEFYB.js} +2 -2
- package/dist/connection-token-resolver.d.ts +4 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +164 -14
- package/dist/index.js +7327 -5442
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +9 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/model-catalog.d.ts +153 -0
- package/dist/new-session-drafts.d.ts +12 -0
- package/dist/organization-membership-lifecycle.d.ts +8 -1
- package/dist/organization-model-providers.d.ts +101 -0
- package/dist/pack-components.d.ts +1 -0
- package/dist/pr-review.d.ts +9 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +7 -7
- package/dist/schema.d.ts +989 -2
- package/dist/schema.js +11 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-control.d.ts +35 -0
- package/dist/session-queue-commands.d.ts +17 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/xai-subscription.d.ts +8 -0
- package/drizzle/0387_bound_host_export_session_payloads.sql +165 -0
- package/drizzle/0388_sandbox_provider_deadline_interactions.sql +491 -0
- package/drizzle/0389_model_catalog_and_gateway_custom_models.sql +241 -0
- package/drizzle/0390_organization_model_provider_connections.sql +197 -0
- package/drizzle/0391_sandbox_provider_deadline_interaction_followup.sql +295 -0
- package/drizzle/0392_context_compaction_pending_observability.sql +198 -0
- package/drizzle/0393_workspace_memory_and_learning_defaults.sql +102 -0
- package/drizzle/0394_session_selected_skill_activation.sql +20 -0
- package/drizzle/0395_scheduled_task_unclaimed_occurrence_invalidation.sql +154 -0
- package/drizzle/0396_model_call_equivalent_credit_cost.sql +28 -0
- package/drizzle/0397_sandbox_deadline_rotation_preemption.sql +113 -0
- package/drizzle/0398_organization_workspace_management_entry.sql +206 -0
- package/drizzle/0399_additional_managed_organization_creation.sql +691 -0
- package/package.json +6 -6
- package/src/automations.ts +9 -1
- package/src/connection-token-resolver.ts +12 -0
- package/src/index.ts +1551 -272
- package/src/insights-model-bundle.ts +54 -5
- package/src/insights.ts +53 -7
- package/src/model-catalog.ts +682 -0
- package/src/new-session-drafts.ts +66 -21
- package/src/organization-membership-lifecycle.ts +47 -5
- package/src/organization-model-providers.ts +614 -0
- package/src/pack-components.ts +15 -2
- package/src/pr-review.ts +19 -0
- package/src/provision-roles.ts +2 -0
- package/src/runtime-posture.ts +48 -0
- package/src/schema.ts +348 -32
- package/src/session-control.ts +170 -0
- package/src/session-queue-commands.ts +157 -82
- package/src/xai-subscription.ts +66 -14
- package/dist/chunk-4NAR7FBA.js.map +0 -1
- package/dist/chunk-O56DM5GZ.js.map +0 -1
- /package/dist/{chunk-SZAWDXQC.js.map → chunk-5S4BQAHX.js.map} +0 -0
- /package/dist/{chunk-S3TDDCS2.js.map → chunk-CITYUGMW.js.map} +0 -0
- /package/dist/{chunk-F6PBS6WE.js.map → chunk-HZV7O6JR.js.map} +0 -0
- /package/dist/{chunk-5NNCGFQY.js.map → chunk-RLQVPOVR.js.map} +0 -0
- /package/dist/{chunk-DZCYW2WT.js.map → chunk-UKOGHUGC.js.map} +0 -0
- /package/dist/{chunk-US4IS6IV.js.map → chunk-X6TO5HII.js.map} +0 -0
- /package/dist/{chunk-U4WPPEBH.js.map → chunk-Y53WEFYB.js.map} +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Migration 0389: deployment-owned model catalog authority, provider-scoped
|
|
3
|
+
-- workspace Vercel AI Gateway/OpenRouter slugs, and indexed pre-catalog prompt
|
|
4
|
+
-- receipt replay.
|
|
5
|
+
-- This changes the exact runtime-posture table/grant contract. Stop every API,
|
|
6
|
+
-- control worker, and turn worker before applying it, and never restart a
|
|
7
|
+
-- pre-0389 image after commit.
|
|
8
|
+
|
|
9
|
+
SET LOCAL lock_timeout = '5s';
|
|
10
|
+
SET LOCAL statement_timeout = '5min';
|
|
11
|
+
|
|
12
|
+
DO $model_catalog_runtime_drain_before$
|
|
13
|
+
DECLARE
|
|
14
|
+
configured_roles_text text := nullif(
|
|
15
|
+
current_setting('opengeni.migration_application_roles', true), ''
|
|
16
|
+
);
|
|
17
|
+
configured_roles jsonb;
|
|
18
|
+
BEGIN
|
|
19
|
+
IF configured_roles_text IS NULL THEN
|
|
20
|
+
RAISE EXCEPTION
|
|
21
|
+
'0389 model catalog activation requires an explicit application database role list'
|
|
22
|
+
USING ERRCODE = '55000';
|
|
23
|
+
END IF;
|
|
24
|
+
BEGIN
|
|
25
|
+
configured_roles := configured_roles_text::jsonb;
|
|
26
|
+
EXCEPTION WHEN OTHERS THEN
|
|
27
|
+
RAISE EXCEPTION
|
|
28
|
+
'0389 model catalog activation received a malformed application database role list'
|
|
29
|
+
USING ERRCODE = '55000';
|
|
30
|
+
END;
|
|
31
|
+
IF jsonb_typeof(configured_roles) <> 'array'
|
|
32
|
+
OR jsonb_array_length(configured_roles) NOT BETWEEN 1 AND 16
|
|
33
|
+
OR EXISTS (
|
|
34
|
+
SELECT 1 FROM jsonb_array_elements(configured_roles) AS roles(value)
|
|
35
|
+
WHERE jsonb_typeof(value) <> 'string'
|
|
36
|
+
OR btrim(value #>> '{}') = ''
|
|
37
|
+
OR octet_length(value #>> '{}') > 63
|
|
38
|
+
)
|
|
39
|
+
OR (
|
|
40
|
+
SELECT count(*) FROM jsonb_array_elements_text(configured_roles)
|
|
41
|
+
) <> (
|
|
42
|
+
SELECT count(DISTINCT value)
|
|
43
|
+
FROM jsonb_array_elements_text(configured_roles) AS roles(value)
|
|
44
|
+
)
|
|
45
|
+
THEN
|
|
46
|
+
RAISE EXCEPTION
|
|
47
|
+
'0389 model catalog activation received an invalid application database role list'
|
|
48
|
+
USING ERRCODE = '55000';
|
|
49
|
+
END IF;
|
|
50
|
+
IF EXISTS (
|
|
51
|
+
SELECT 1 FROM pg_stat_activity activity
|
|
52
|
+
JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
|
|
53
|
+
ON roles.role_name = activity.usename
|
|
54
|
+
WHERE activity.datname = current_database()
|
|
55
|
+
AND activity.pid <> pg_backend_pid()
|
|
56
|
+
)
|
|
57
|
+
THEN
|
|
58
|
+
RAISE EXCEPTION
|
|
59
|
+
'0389 model catalog activation requires all configured OpenGeni application database sessions to be stopped'
|
|
60
|
+
USING ERRCODE = '55000';
|
|
61
|
+
END IF;
|
|
62
|
+
END
|
|
63
|
+
$model_catalog_runtime_drain_before$;
|
|
64
|
+
|
|
65
|
+
-- Exactly one deployment-global, secret-free catalog document. Ordinary API
|
|
66
|
+
-- and worker roles may only read it; the operator upsert command uses a
|
|
67
|
+
-- migration/admin credential. No seed or boot-time rewrite is intentional.
|
|
68
|
+
CREATE TABLE deployment_model_catalog (
|
|
69
|
+
singleton boolean PRIMARY KEY DEFAULT true,
|
|
70
|
+
document jsonb NOT NULL,
|
|
71
|
+
version bigint NOT NULL DEFAULT 1,
|
|
72
|
+
updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
73
|
+
CONSTRAINT deployment_model_catalog_singleton_chk CHECK (singleton),
|
|
74
|
+
CONSTRAINT deployment_model_catalog_document_chk CHECK (jsonb_typeof(document) = 'object'),
|
|
75
|
+
CONSTRAINT deployment_model_catalog_version_chk CHECK (version > 0)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
COMMENT ON TABLE deployment_model_catalog IS
|
|
79
|
+
'Deployment-global, secret-free model catalog singleton. Runtime is SELECT-only; operators update it explicitly.';
|
|
80
|
+
|
|
81
|
+
CREATE TABLE workspace_gateway_custom_models (
|
|
82
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
83
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
84
|
+
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
85
|
+
provider_kind text NOT NULL,
|
|
86
|
+
upstream_model_id text NOT NULL,
|
|
87
|
+
label text,
|
|
88
|
+
version integer NOT NULL DEFAULT 1,
|
|
89
|
+
create_operation_id uuid NOT NULL,
|
|
90
|
+
create_request_hash text NOT NULL,
|
|
91
|
+
delete_operation_id uuid,
|
|
92
|
+
delete_request_hash text,
|
|
93
|
+
created_by_subject_id text NOT NULL,
|
|
94
|
+
retired_at timestamptz,
|
|
95
|
+
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
96
|
+
updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
97
|
+
CONSTRAINT workspace_gateway_custom_models_workspace_account_fk
|
|
98
|
+
FOREIGN KEY (workspace_id, account_id)
|
|
99
|
+
REFERENCES workspaces(id, account_id) ON DELETE CASCADE,
|
|
100
|
+
CONSTRAINT workspace_gateway_custom_models_provider_kind_chk CHECK (
|
|
101
|
+
provider_kind IN ('vercel_gateway', 'openrouter')
|
|
102
|
+
),
|
|
103
|
+
CONSTRAINT workspace_gateway_custom_models_upstream_chk CHECK (
|
|
104
|
+
octet_length(upstream_model_id) BETWEEN 1 AND 238
|
|
105
|
+
AND upstream_model_id ~ '^[!-~]+$'
|
|
106
|
+
AND upstream_model_id !~ '[|]'
|
|
107
|
+
),
|
|
108
|
+
CONSTRAINT workspace_gateway_custom_models_label_chk CHECK (
|
|
109
|
+
label IS NULL OR (
|
|
110
|
+
octet_length(label) BETWEEN 1 AND 128
|
|
111
|
+
AND label !~ '[\r\n|]'
|
|
112
|
+
)
|
|
113
|
+
),
|
|
114
|
+
CONSTRAINT workspace_gateway_custom_models_actor_chk CHECK (
|
|
115
|
+
octet_length(created_by_subject_id) BETWEEN 1 AND 1024
|
|
116
|
+
),
|
|
117
|
+
CONSTRAINT workspace_gateway_custom_models_version_chk CHECK (version > 0),
|
|
118
|
+
CONSTRAINT workspace_gateway_custom_models_create_hash_chk CHECK (
|
|
119
|
+
create_request_hash ~ '^[a-f0-9]{64}$'
|
|
120
|
+
),
|
|
121
|
+
CONSTRAINT workspace_gateway_custom_models_delete_receipt_chk CHECK (
|
|
122
|
+
(delete_operation_id IS NULL AND delete_request_hash IS NULL)
|
|
123
|
+
OR (
|
|
124
|
+
delete_operation_id IS NOT NULL
|
|
125
|
+
AND delete_request_hash ~ '^[a-f0-9]{64}$'
|
|
126
|
+
AND retired_at IS NOT NULL
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
CREATE UNIQUE INDEX workspace_gateway_custom_models_workspace_upstream_uq
|
|
132
|
+
ON workspace_gateway_custom_models (workspace_id, provider_kind, upstream_model_id)
|
|
133
|
+
WHERE retired_at IS NULL;
|
|
134
|
+
|
|
135
|
+
CREATE UNIQUE INDEX workspace_gateway_custom_models_create_operation_uq
|
|
136
|
+
ON workspace_gateway_custom_models (workspace_id, provider_kind, create_operation_id);
|
|
137
|
+
|
|
138
|
+
CREATE UNIQUE INDEX workspace_gateway_custom_models_delete_operation_uq
|
|
139
|
+
ON workspace_gateway_custom_models (workspace_id, provider_kind, delete_operation_id)
|
|
140
|
+
WHERE delete_operation_id IS NOT NULL;
|
|
141
|
+
|
|
142
|
+
-- New Send/Steer receipts persist one parsed boundary-request fingerprint and
|
|
143
|
+
-- replay it before mutable model-catalog resolution. Legacy receipt identity
|
|
144
|
+
-- remains unchanged; this partial index keeps the actor/key compatibility
|
|
145
|
+
-- probe bounded even when the prior action or target session differs.
|
|
146
|
+
CREATE INDEX session_command_receipts_prompt_actor_operation_idx
|
|
147
|
+
ON session_command_receipts (
|
|
148
|
+
workspace_id,
|
|
149
|
+
actor_type,
|
|
150
|
+
actor_subject_id,
|
|
151
|
+
actor_attempt_id,
|
|
152
|
+
operation_key
|
|
153
|
+
)
|
|
154
|
+
WHERE action IN ('prompt.send', 'prompt.steer');
|
|
155
|
+
|
|
156
|
+
ALTER TABLE workspace_gateway_custom_models ENABLE ROW LEVEL SECURITY;
|
|
157
|
+
ALTER TABLE workspace_gateway_custom_models FORCE ROW LEVEL SECURITY;
|
|
158
|
+
CREATE POLICY workspace_isolation ON workspace_gateway_custom_models
|
|
159
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
160
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
161
|
+
|
|
162
|
+
-- Migration 0040 registers owner default privileges for the historical
|
|
163
|
+
-- opengeni_app role. Rotated-role deployments must not inherit that stale
|
|
164
|
+
-- grant on these new tables, and host defaults may name other obsolete
|
|
165
|
+
-- application roles. Strip every explicit non-owner table grantee. The
|
|
166
|
+
-- migration_application_roles list above is drain detection only; the
|
|
167
|
+
-- post-migration role provisioner grants only the deployment's current target
|
|
168
|
+
-- runtime role.
|
|
169
|
+
DO $model_catalog_table_acl_reset$
|
|
170
|
+
DECLARE
|
|
171
|
+
data_schema text := pg_catalog.current_schema();
|
|
172
|
+
relation_name text;
|
|
173
|
+
role_name text;
|
|
174
|
+
BEGIN
|
|
175
|
+
FOR relation_name IN
|
|
176
|
+
SELECT relation.value
|
|
177
|
+
FROM pg_catalog.unnest(
|
|
178
|
+
ARRAY[
|
|
179
|
+
'deployment_model_catalog',
|
|
180
|
+
'workspace_gateway_custom_models'
|
|
181
|
+
]::text[]
|
|
182
|
+
) AS relation(value)
|
|
183
|
+
LOOP
|
|
184
|
+
EXECUTE pg_catalog.format(
|
|
185
|
+
'REVOKE ALL ON TABLE %I.%I FROM PUBLIC',
|
|
186
|
+
data_schema,
|
|
187
|
+
relation_name
|
|
188
|
+
);
|
|
189
|
+
FOR role_name IN
|
|
190
|
+
SELECT grantee_role.rolname
|
|
191
|
+
FROM pg_catalog.pg_class relation
|
|
192
|
+
CROSS JOIN LATERAL pg_catalog.aclexplode(
|
|
193
|
+
coalesce(
|
|
194
|
+
relation.relacl,
|
|
195
|
+
pg_catalog.acldefault('r', relation.relowner)
|
|
196
|
+
)
|
|
197
|
+
) privilege
|
|
198
|
+
INNER JOIN pg_catalog.pg_roles grantee_role
|
|
199
|
+
ON grantee_role.oid = privilege.grantee
|
|
200
|
+
WHERE relation.oid = pg_catalog.to_regclass(
|
|
201
|
+
pg_catalog.format('%I.%I', data_schema, relation_name)
|
|
202
|
+
)
|
|
203
|
+
AND privilege.grantee <> 0
|
|
204
|
+
AND privilege.grantee <> relation.relowner
|
|
205
|
+
GROUP BY grantee_role.rolname
|
|
206
|
+
ORDER BY grantee_role.rolname COLLATE "C"
|
|
207
|
+
LOOP
|
|
208
|
+
EXECUTE pg_catalog.format(
|
|
209
|
+
'REVOKE ALL ON TABLE %I.%I FROM %I',
|
|
210
|
+
data_schema,
|
|
211
|
+
relation_name,
|
|
212
|
+
role_name
|
|
213
|
+
);
|
|
214
|
+
END LOOP;
|
|
215
|
+
END LOOP;
|
|
216
|
+
END
|
|
217
|
+
$model_catalog_table_acl_reset$;
|
|
218
|
+
|
|
219
|
+
COMMENT ON TABLE workspace_gateway_custom_models IS
|
|
220
|
+
'Workspace-owned Vercel AI Gateway and OpenRouter upstream slugs. Retired rows remain execution evidence; capabilities and billing are never stored here.';
|
|
221
|
+
|
|
222
|
+
DO $model_catalog_runtime_drain_after$
|
|
223
|
+
DECLARE
|
|
224
|
+
configured_roles jsonb := current_setting(
|
|
225
|
+
'opengeni.migration_application_roles', false
|
|
226
|
+
)::jsonb;
|
|
227
|
+
BEGIN
|
|
228
|
+
IF EXISTS (
|
|
229
|
+
SELECT 1 FROM pg_stat_activity activity
|
|
230
|
+
JOIN jsonb_array_elements_text(configured_roles) roles(role_name)
|
|
231
|
+
ON roles.role_name = activity.usename
|
|
232
|
+
WHERE activity.datname = current_database()
|
|
233
|
+
AND activity.pid <> pg_backend_pid()
|
|
234
|
+
)
|
|
235
|
+
THEN
|
|
236
|
+
RAISE EXCEPTION
|
|
237
|
+
'0389 model catalog activation observed a configured OpenGeni application database session after schema installation'
|
|
238
|
+
USING ERRCODE = '55000';
|
|
239
|
+
END IF;
|
|
240
|
+
END
|
|
241
|
+
$model_catalog_runtime_drain_after$;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Organization-owned Vercel AI Gateway/OpenRouter credentials and model catalog.
|
|
3
|
+
-- Personal workspaces are deliberately excluded from runtime inheritance.
|
|
4
|
+
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '5min';
|
|
7
|
+
|
|
8
|
+
DO $scope_visibility$
|
|
9
|
+
DECLARE data_schema text := current_schema();
|
|
10
|
+
BEGIN
|
|
11
|
+
EXECUTE format($ddl$
|
|
12
|
+
CREATE OR REPLACE FUNCTION opengeni_private.organization_model_provider_scope_visible(
|
|
13
|
+
p_account_id uuid
|
|
14
|
+
) RETURNS boolean
|
|
15
|
+
LANGUAGE plpgsql SECURITY DEFINER
|
|
16
|
+
SET search_path = pg_catalog
|
|
17
|
+
AS $function$
|
|
18
|
+
DECLARE
|
|
19
|
+
workspace_value uuid := opengeni_private.current_workspace_id();
|
|
20
|
+
subject_value text := opengeni_private.current_subject_id();
|
|
21
|
+
visible boolean := false;
|
|
22
|
+
previous_lifecycle text := pg_catalog.current_setting(
|
|
23
|
+
'opengeni.organization_tenancy_lifecycle', true
|
|
24
|
+
);
|
|
25
|
+
BEGIN
|
|
26
|
+
IF p_account_id IS NULL
|
|
27
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
28
|
+
THEN RETURN false; END IF;
|
|
29
|
+
|
|
30
|
+
PERFORM pg_catalog.set_config(
|
|
31
|
+
'opengeni.organization_tenancy_lifecycle',
|
|
32
|
+
'organization_membership_lifecycle', true
|
|
33
|
+
);
|
|
34
|
+
IF workspace_value IS NOT NULL THEN
|
|
35
|
+
SELECT EXISTS (
|
|
36
|
+
SELECT 1 FROM %1$I.workspaces workspace
|
|
37
|
+
WHERE workspace.account_id = p_account_id AND workspace.id = workspace_value
|
|
38
|
+
) AND NOT EXISTS (
|
|
39
|
+
SELECT 1 FROM %1$I.organization_memberships membership
|
|
40
|
+
WHERE membership.account_id = p_account_id
|
|
41
|
+
AND membership.personal_workspace_id = workspace_value
|
|
42
|
+
) INTO visible;
|
|
43
|
+
ELSIF subject_value IS NOT NULL AND (
|
|
44
|
+
subject_value LIKE 'user:%%'
|
|
45
|
+
OR (
|
|
46
|
+
subject_value = 'dev'
|
|
47
|
+
AND EXISTS (
|
|
48
|
+
SELECT 1 FROM %1$I.managed_accounts account
|
|
49
|
+
WHERE account.id = p_account_id
|
|
50
|
+
AND account.external_source = 'opengeni:local'
|
|
51
|
+
AND account.external_id = 'default'
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
) THEN
|
|
55
|
+
SELECT EXISTS (
|
|
56
|
+
SELECT 1 FROM %1$I.organization_memberships membership
|
|
57
|
+
WHERE membership.account_id = p_account_id
|
|
58
|
+
AND membership.subject_id = subject_value
|
|
59
|
+
AND membership.status = 'active'
|
|
60
|
+
AND membership.role IN ('owner', 'admin')
|
|
61
|
+
) INTO visible;
|
|
62
|
+
END IF;
|
|
63
|
+
PERFORM pg_catalog.set_config(
|
|
64
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
65
|
+
);
|
|
66
|
+
RETURN coalesce(visible, false);
|
|
67
|
+
EXCEPTION WHEN OTHERS THEN
|
|
68
|
+
PERFORM pg_catalog.set_config(
|
|
69
|
+
'opengeni.organization_tenancy_lifecycle', coalesce(previous_lifecycle, ''), true
|
|
70
|
+
);
|
|
71
|
+
RAISE;
|
|
72
|
+
END
|
|
73
|
+
$function$;
|
|
74
|
+
$ddl$, data_schema);
|
|
75
|
+
END
|
|
76
|
+
$scope_visibility$;
|
|
77
|
+
|
|
78
|
+
CREATE TABLE organization_model_provider_connections (
|
|
79
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
80
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
81
|
+
provider_kind text NOT NULL CHECK (provider_kind IN ('vercel_gateway', 'openrouter')),
|
|
82
|
+
status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'revoked')),
|
|
83
|
+
credential_encrypted text NOT NULL,
|
|
84
|
+
version integer NOT NULL DEFAULT 1 CHECK (version > 0),
|
|
85
|
+
operation_id uuid NOT NULL,
|
|
86
|
+
request_hash text NOT NULL CHECK (request_hash ~ '^[a-f0-9]{64}$'),
|
|
87
|
+
updated_by_subject_id text NOT NULL CHECK (octet_length(updated_by_subject_id) BETWEEN 1 AND 1024),
|
|
88
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
89
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
90
|
+
UNIQUE (account_id, provider_kind),
|
|
91
|
+
UNIQUE (account_id, provider_kind, operation_id)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
CREATE TABLE organization_model_provider_connection_operations (
|
|
95
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
96
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
97
|
+
provider_kind text NOT NULL CHECK (provider_kind IN ('vercel_gateway', 'openrouter')),
|
|
98
|
+
operation_id uuid NOT NULL,
|
|
99
|
+
request_hash text NOT NULL CHECK (request_hash ~ '^[a-f0-9]{64}$'),
|
|
100
|
+
result_status text NOT NULL CHECK (result_status IN ('active', 'revoked')),
|
|
101
|
+
result_version integer NOT NULL CHECK (result_version > 0),
|
|
102
|
+
result_created_at timestamptz NOT NULL,
|
|
103
|
+
result_updated_at timestamptz NOT NULL,
|
|
104
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
105
|
+
UNIQUE (account_id, provider_kind, operation_id)
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
CREATE TABLE organization_model_provider_custom_models (
|
|
109
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
110
|
+
account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
|
|
111
|
+
provider_kind text NOT NULL CHECK (provider_kind IN ('vercel_gateway', 'openrouter')),
|
|
112
|
+
upstream_model_id text NOT NULL CHECK (
|
|
113
|
+
octet_length(upstream_model_id) BETWEEN 1 AND 238
|
|
114
|
+
AND upstream_model_id ~ '^[!-~]+$' AND upstream_model_id !~ '[|]'
|
|
115
|
+
),
|
|
116
|
+
label text CHECK (
|
|
117
|
+
label IS NULL OR (octet_length(label) BETWEEN 1 AND 128 AND label !~ '[\r\n|]')
|
|
118
|
+
),
|
|
119
|
+
version integer NOT NULL DEFAULT 1 CHECK (version > 0),
|
|
120
|
+
create_operation_id uuid NOT NULL,
|
|
121
|
+
create_request_hash text NOT NULL CHECK (create_request_hash ~ '^[a-f0-9]{64}$'),
|
|
122
|
+
delete_operation_id uuid,
|
|
123
|
+
delete_request_hash text,
|
|
124
|
+
created_by_subject_id text NOT NULL CHECK (octet_length(created_by_subject_id) BETWEEN 1 AND 1024),
|
|
125
|
+
retired_at timestamptz,
|
|
126
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
127
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
128
|
+
CHECK (
|
|
129
|
+
(delete_operation_id IS NULL AND delete_request_hash IS NULL)
|
|
130
|
+
OR (delete_operation_id IS NOT NULL AND delete_request_hash ~ '^[a-f0-9]{64}$' AND retired_at IS NOT NULL)
|
|
131
|
+
),
|
|
132
|
+
UNIQUE (account_id, provider_kind, create_operation_id)
|
|
133
|
+
);
|
|
134
|
+
CREATE UNIQUE INDEX organization_model_provider_custom_models_active_uq
|
|
135
|
+
ON organization_model_provider_custom_models(account_id, provider_kind, upstream_model_id)
|
|
136
|
+
WHERE retired_at IS NULL;
|
|
137
|
+
CREATE UNIQUE INDEX organization_model_provider_custom_models_delete_operation_uq
|
|
138
|
+
ON organization_model_provider_custom_models(account_id, provider_kind, delete_operation_id)
|
|
139
|
+
WHERE delete_operation_id IS NOT NULL;
|
|
140
|
+
|
|
141
|
+
ALTER TABLE organization_model_provider_connections ENABLE ROW LEVEL SECURITY;
|
|
142
|
+
ALTER TABLE organization_model_provider_connections FORCE ROW LEVEL SECURITY;
|
|
143
|
+
ALTER TABLE organization_model_provider_connection_operations ENABLE ROW LEVEL SECURITY;
|
|
144
|
+
ALTER TABLE organization_model_provider_connection_operations FORCE ROW LEVEL SECURITY;
|
|
145
|
+
ALTER TABLE organization_model_provider_custom_models ENABLE ROW LEVEL SECURITY;
|
|
146
|
+
ALTER TABLE organization_model_provider_custom_models FORCE ROW LEVEL SECURITY;
|
|
147
|
+
|
|
148
|
+
CREATE POLICY organization_model_provider_scope
|
|
149
|
+
ON organization_model_provider_connections
|
|
150
|
+
USING (opengeni_private.organization_model_provider_scope_visible(account_id))
|
|
151
|
+
WITH CHECK (opengeni_private.organization_model_provider_scope_visible(account_id));
|
|
152
|
+
CREATE POLICY organization_model_provider_scope
|
|
153
|
+
ON organization_model_provider_connection_operations
|
|
154
|
+
USING (opengeni_private.organization_model_provider_scope_visible(account_id))
|
|
155
|
+
WITH CHECK (opengeni_private.organization_model_provider_scope_visible(account_id));
|
|
156
|
+
CREATE POLICY organization_model_provider_scope
|
|
157
|
+
ON organization_model_provider_custom_models
|
|
158
|
+
USING (opengeni_private.organization_model_provider_scope_visible(account_id))
|
|
159
|
+
WITH CHECK (opengeni_private.organization_model_provider_scope_visible(account_id));
|
|
160
|
+
|
|
161
|
+
REVOKE ALL ON organization_model_provider_connections FROM PUBLIC;
|
|
162
|
+
REVOKE ALL ON organization_model_provider_connection_operations FROM PUBLIC;
|
|
163
|
+
REVOKE ALL ON organization_model_provider_custom_models FROM PUBLIC;
|
|
164
|
+
REVOKE ALL ON FUNCTION opengeni_private.organization_model_provider_scope_visible(uuid) FROM PUBLIC;
|
|
165
|
+
|
|
166
|
+
DO $application_grants$
|
|
167
|
+
DECLARE
|
|
168
|
+
data_schema text := current_schema();
|
|
169
|
+
application_role text;
|
|
170
|
+
BEGIN
|
|
171
|
+
FOR application_role IN
|
|
172
|
+
SELECT role_value.rolname
|
|
173
|
+
FROM pg_catalog.jsonb_array_elements_text(
|
|
174
|
+
coalesce(nullif(current_setting('opengeni.migration_application_roles', true), ''), '[]')::jsonb
|
|
175
|
+
) configured(value)
|
|
176
|
+
JOIN pg_catalog.pg_roles role_value ON role_value.rolname = configured.value
|
|
177
|
+
UNION SELECT 'opengeni_app'
|
|
178
|
+
WHERE EXISTS (SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = 'opengeni_app')
|
|
179
|
+
LOOP
|
|
180
|
+
EXECUTE format(
|
|
181
|
+
'GRANT SELECT, INSERT, UPDATE ON TABLE %1$I.organization_model_provider_connections, %1$I.organization_model_provider_connection_operations, %1$I.organization_model_provider_custom_models TO %2$I',
|
|
182
|
+
data_schema, application_role
|
|
183
|
+
);
|
|
184
|
+
EXECUTE format(
|
|
185
|
+
'GRANT EXECUTE ON FUNCTION opengeni_private.organization_model_provider_scope_visible(uuid) TO %I',
|
|
186
|
+
application_role
|
|
187
|
+
);
|
|
188
|
+
END LOOP;
|
|
189
|
+
END
|
|
190
|
+
$application_grants$;
|
|
191
|
+
|
|
192
|
+
COMMENT ON TABLE organization_model_provider_connections IS
|
|
193
|
+
'Encrypted organization-owned Vercel AI Gateway/OpenRouter credentials inherited by shared workspaces only.';
|
|
194
|
+
COMMENT ON TABLE organization_model_provider_connection_operations IS
|
|
195
|
+
'Durable secret-free idempotency receipts for organization provider connection mutations.';
|
|
196
|
+
COMMENT ON TABLE organization_model_provider_custom_models IS
|
|
197
|
+
'Retained organization provider model generations; active rows are fresh-selection authority.';
|