@opengeni/db 5.0.1 → 6.0.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 +0 -2
- package/dist/canonical-human-identities.d.ts +4 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-components.d.ts +5 -13
- package/dist/capability-integrations.d.ts +23 -8
- package/dist/capability-reconciliation.d.ts +5 -0
- package/dist/capability-reconciliation.js +21 -0
- package/dist/capability-reconciliation.js.map +1 -0
- package/dist/{chunk-6T2GGYWE.js → chunk-4UJCXBR2.js} +4 -5800
- package/dist/chunk-4UJCXBR2.js.map +1 -0
- package/dist/{chunk-MWMXWHJD.js → chunk-7JCDSMX2.js} +2 -2
- package/dist/chunk-AJA2ZIUW.js +219 -0
- package/dist/chunk-AJA2ZIUW.js.map +1 -0
- package/dist/chunk-CDAT6EKW.js +247 -0
- package/dist/chunk-CDAT6EKW.js.map +1 -0
- package/dist/{chunk-DPONPYJD.js → chunk-CJI6G254.js} +70 -18
- package/dist/chunk-CJI6G254.js.map +1 -0
- package/dist/chunk-G32NVMRJ.js +276 -0
- package/dist/chunk-G32NVMRJ.js.map +1 -0
- package/dist/chunk-GABXCG3N.js +5811 -0
- package/dist/chunk-GABXCG3N.js.map +1 -0
- package/dist/{chunk-ERKVSPDX.js → chunk-LVY33EWL.js} +3 -3
- package/dist/{chunk-45CP2YKY.js → chunk-MAMBI2HR.js} +9 -3
- package/dist/{chunk-45CP2YKY.js.map → chunk-MAMBI2HR.js.map} +1 -1
- package/dist/{chunk-F73UKJY6.js → chunk-MN5PZMEK.js} +91 -108
- package/dist/chunk-MN5PZMEK.js.map +1 -0
- package/dist/chunk-PJI4MUZ7.js +131 -0
- package/dist/chunk-PJI4MUZ7.js.map +1 -0
- package/dist/{chunk-T6BWIGWJ.js → chunk-RIYHP2WU.js} +2 -2
- package/dist/{chunk-7HMK3B45.js → chunk-RL2MUXM3.js} +16 -7
- package/dist/chunk-RL2MUXM3.js.map +1 -0
- package/dist/{chunk-HQDVI2DB.js → chunk-SYKHLPTK.js} +4 -3
- package/dist/{chunk-HQDVI2DB.js.map → chunk-SYKHLPTK.js.map} +1 -1
- package/dist/{chunk-T6YLHJLS.js → chunk-XIXVVBW2.js} +198 -51
- package/dist/chunk-XIXVVBW2.js.map +1 -0
- package/dist/chunk-ZPJ6V2VG.js +156 -0
- package/dist/chunk-ZPJ6V2VG.js.map +1 -0
- package/dist/codex-capacity-recovery.d.ts +12 -0
- package/dist/codex-failure-eligibility.d.ts +9 -0
- package/dist/company-profile-schema.d.ts +4 -2
- package/dist/connect-attempts.d.ts +15 -0
- package/dist/connect-attempts.js +25 -0
- package/dist/connect-attempts.js.map +1 -0
- package/dist/connection-authority.d.ts +7 -63
- package/dist/connection-refresh-lock.d.ts +4 -0
- package/dist/connection-token-resolver.d.ts +8 -43
- package/dist/database-timing.d.ts +10 -0
- package/dist/database.d.ts +4 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.d.ts +9 -0
- package/dist/editable-artifacts.js +7 -5
- package/dist/index.d.ts +447 -78
- package/dist/index.js +12705 -12872
- package/dist/index.js.map +1 -1
- package/dist/insights-usage-bundle.d.ts +3 -0
- package/dist/integration-bindings.d.ts +1 -1
- package/dist/integration-facets.d.ts +18 -1
- package/dist/knowledge-entries-schema.d.ts +2 -0
- package/dist/knowledge-entries.d.ts +1 -0
- package/dist/knowledge-indexing.d.ts +1 -0
- package/dist/live-session-attempt.d.ts +35 -137
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-account-bindings.d.ts +4 -0
- package/dist/mcp-oauth.d.ts +11 -0
- package/dist/mcp-operations.js +2 -2
- package/dist/migrate.js +1 -1
- package/dist/organization-integration-policy.d.ts +51 -0
- package/dist/organization-integration-policy.js +18 -0
- package/dist/organization-integration-policy.js.map +1 -0
- package/dist/organization-usage.d.ts +20 -0
- package/dist/plugin-packages.d.ts +9 -12
- package/dist/pr-review.d.ts +0 -11
- package/dist/preference-registry-schema.d.ts +17 -0
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.d.ts +124 -6
- package/dist/retained-provider-commands.js +29 -97
- package/dist/retained-provider-commands.js.map +1 -1
- package/dist/retired-session-create-metadata.d.ts +4 -0
- package/dist/runtime-posture.d.ts +7 -5
- package/dist/schema.d.ts +876 -656
- package/dist/schema.js +5 -7
- package/dist/session-admission-block.d.ts +32 -0
- package/dist/session-attempt-writers.d.ts +4 -0
- package/dist/session-background-commands.js +5 -4
- package/dist/session-command-output.js +8 -126
- package/dist/session-command-output.js.map +1 -1
- package/dist/session-control.d.ts +5 -1
- package/dist/session-event-slices.d.ts +10 -0
- package/dist/session-event-slices.js +8 -222
- package/dist/session-event-slices.js.map +1 -1
- package/dist/session-file-attachments.d.ts +28 -0
- package/dist/session-goal-reports.d.ts +43 -0
- package/dist/session-mcp-approval.d.ts +4 -0
- package/dist/session-mcp-credential-rotation.js +5 -4
- package/dist/session-mcp-credential-rotation.js.map +1 -1
- package/dist/session-message-search.d.ts +22 -0
- package/dist/session-queue-commands.d.ts +2 -6
- package/dist/session-retry.d.ts +36 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/skill-source-release-impact.d.ts +18 -0
- package/dist/skill-source-release.d.ts +9 -0
- package/dist/skills.d.ts +7 -1
- package/dist/user-resource-authority.d.ts +0 -8
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +5 -4
- package/dist/workspace-tool-defaults.js.map +1 -1
- package/drizzle/0468_knowledge_relationship_projection.sql +30 -0
- package/drizzle/0469_knowledge_source_discovery.sql +85 -0
- package/drizzle/0470_organization_integration_policy.sql +151 -0
- package/drizzle/0471_usage_events_account_recent_index.sql +4 -0
- package/drizzle/0472_usage_events_workspace_recent_index.sql +4 -0
- package/drizzle/0473_organization_usage_analytical_capability.sql +237 -0
- package/drizzle/0474_goal_report_requirements.sql +156 -0
- package/drizzle/0475_session_event_history_statistics.sql +15 -0
- package/drizzle/0476_session_event_history_policy_planning.sql +29 -0
- package/drizzle/0477_managed_sign_in_methods.sql +303 -0
- package/drizzle/0478_sender_owned_connections.sql +2772 -0
- package/drizzle/0479_connection_create_idempotency.sql +16 -0
- package/drizzle/0480_private_session_organization_key_administration.sql +154 -0
- package/drizzle/0481_personal_connection_visibility_cleanup.sql +45 -0
- package/drizzle/0482_remove_packs.sql +417 -0
- package/drizzle/0483_preclaim_admission_block.sql +85 -0
- package/drizzle/0484_insights_usage_projection.sql +211 -0
- package/drizzle/0485_inherited_mcp_approval_policies.sql +6 -0
- package/drizzle/0486_reasoning_configuration_message_forks.sql +35 -0
- package/drizzle/0487_skill_catalog_message_forks.sql +26 -0
- package/drizzle/0488_permanent_skill_removal.sql +207 -0
- package/drizzle/0489_modal_command_byte_offsets.sql +17 -0
- package/drizzle/0490_periodic_capture_attempt_cadence.sql +6 -0
- package/drizzle/0491_warm_capture_holder_reclamation.sql +57 -0
- package/drizzle/0492_codex_accepted_source_authority.sql +249 -0
- package/drizzle/0493_stopping_command_error_containment.sql +58 -0
- package/drizzle/0494_mcp_account_bindings.sql +334 -0
- package/drizzle/0495_consented_sandbox_recovery.sql +220 -0
- package/drizzle/0496_supervised_command_settlement.sql +386 -0
- package/drizzle/0497_modal_inventory_owner_read_capability.sql +112 -0
- package/drizzle/0498_enrollment_membership_fence.sql +87 -0
- package/drizzle/0499_session_attachment_access.sql +224 -0
- package/drizzle/0500_session_execution_epoch.sql +37 -0
- package/drizzle/0501_session_sharing_execution.sql +591 -0
- package/drizzle/0502_active_message_boundary_forks.sql +30 -0
- package/package.json +17 -5
- package/src/automations.ts +17 -73
- package/src/canonical-human-identities.ts +17 -5
- package/src/capability-components.ts +7 -24
- package/src/capability-integrations.ts +301 -21
- package/src/capability-reconciliation.ts +19 -0
- package/src/codex-capacity-recovery.ts +47 -0
- package/src/codex-failure-eligibility.ts +93 -0
- package/src/connect-attempts.ts +49 -18
- package/src/connection-authority.ts +12 -158
- package/src/connection-refresh-lock.ts +22 -0
- package/src/connection-token-resolver.ts +58 -530
- package/src/database-timing.ts +43 -0
- package/src/database.ts +61 -26
- package/src/editable-artifacts.ts +1 -1
- package/src/index.ts +3578 -1273
- package/src/insights-model-bundle.ts +19 -7
- package/src/insights-usage-bundle.ts +16 -19
- package/src/integration-bindings.ts +2 -7
- package/src/integration-facets.ts +73 -6
- package/src/knowledge-entries.ts +8 -1
- package/src/live-session-attempt.ts +0 -22
- package/src/mcp-account-bindings.ts +12 -0
- package/src/mcp-oauth.ts +26 -0
- package/src/organization-integration-policy.ts +235 -0
- package/src/organization-usage.ts +83 -0
- package/src/plugin-packages.ts +226 -42
- package/src/pr-review.ts +7 -69
- package/src/preference-registry-schema.ts +1 -0
- package/src/provision-roles.ts +50 -37
- package/src/retained-provider-commands.ts +286 -9
- package/src/retired-session-create-metadata.ts +16 -0
- package/src/runtime-posture.ts +217 -13
- package/src/schema.ts +104 -120
- package/src/session-admission-block.ts +29 -0
- package/src/session-attempt-writers.ts +48 -0
- package/src/session-control.ts +21 -5
- package/src/session-event-slices.ts +76 -26
- package/src/session-file-attachments.ts +66 -0
- package/src/session-goal-reports.ts +255 -0
- package/src/session-mcp-approval.ts +30 -0
- package/src/session-message-search.ts +348 -0
- package/src/session-queue-commands.ts +60 -36
- package/src/session-retry.ts +319 -0
- package/src/skill-config-migration.ts +11 -2
- package/src/skill-source-release-impact.ts +33 -0
- package/src/skill-source-release.ts +67 -26
- package/src/skills.ts +44 -6
- package/src/user-resource-authority.ts +0 -56
- package/dist/chunk-6T2GGYWE.js.map +0 -1
- package/dist/chunk-7HMK3B45.js.map +0 -1
- package/dist/chunk-DPONPYJD.js.map +0 -1
- package/dist/chunk-F73UKJY6.js.map +0 -1
- package/dist/chunk-T6YLHJLS.js.map +0 -1
- package/dist/host-mcp-binding-match.d.ts +0 -5
- package/dist/host-mcp-bindings.d.ts +0 -79
- package/dist/host-mcp-resolvers.d.ts +0 -47
- package/dist/host-mcp-task-authority.d.ts +0 -43
- package/dist/host-selection-identity.d.ts +0 -6
- package/dist/pack-components.d.ts +0 -83
- package/dist/pack-installations.d.ts +0 -86
- package/src/host-mcp-binding-match.ts +0 -34
- package/src/host-mcp-bindings.ts +0 -777
- package/src/host-mcp-resolvers.ts +0 -266
- package/src/host-mcp-task-authority.ts +0 -384
- package/src/host-selection-identity.ts +0 -29
- package/src/pack-components.ts +0 -980
- package/src/pack-installations.ts +0 -950
- /package/dist/{chunk-MWMXWHJD.js.map → chunk-7JCDSMX2.js.map} +0 -0
- /package/dist/{chunk-ERKVSPDX.js.map → chunk-LVY33EWL.js.map} +0 -0
- /package/dist/{chunk-T6BWIGWJ.js.map → chunk-RIYHP2WU.js.map} +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Creation receipts are internal and survive token refresh and disconnect.
|
|
3
|
+
ALTER TABLE connections
|
|
4
|
+
ADD COLUMN create_operation_id text,
|
|
5
|
+
ADD COLUMN create_request_digest text;
|
|
6
|
+
|
|
7
|
+
ALTER TABLE connections ADD CONSTRAINT connections_create_operation_pair_check
|
|
8
|
+
CHECK (
|
|
9
|
+
(create_operation_id IS NULL AND create_request_digest IS NULL)
|
|
10
|
+
OR (create_operation_id IS NOT NULL AND create_request_digest IS NOT NULL
|
|
11
|
+
AND created_by_subject_id IS NOT NULL)
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE UNIQUE INDEX connections_create_operation_uq
|
|
15
|
+
ON connections (workspace_id, created_by_subject_id, create_operation_id)
|
|
16
|
+
WHERE create_operation_id IS NOT NULL;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Organization administrators may configure the product gate with a live
|
|
3
|
+
-- organization key. This grants no access to private session contents.
|
|
4
|
+
-- Existing human command receipts and replay semantics remain valid.
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '5min';
|
|
7
|
+
|
|
8
|
+
ALTER TABLE organization_private_session_setting_events
|
|
9
|
+
ALTER COLUMN actor_membership_id DROP NOT NULL,
|
|
10
|
+
ADD COLUMN actor_subject_id text,
|
|
11
|
+
ADD CONSTRAINT organization_private_session_setting_events_actor_check CHECK (
|
|
12
|
+
actor_membership_id IS NOT NULL OR
|
|
13
|
+
(actor_subject_id IS NOT NULL AND actor_subject_id LIKE 'api_key:%')
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- Share the existing live administrator fence, not its policy-specific name.
|
|
17
|
+
-- The authenticated API caller establishes provenance; this is a live DB fence.
|
|
18
|
+
CREATE FUNCTION opengeni_private.assert_organization_administrator(
|
|
19
|
+
p_account uuid, p_actor text
|
|
20
|
+
) RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
21
|
+
DECLARE
|
|
22
|
+
previous_marker text := current_setting('opengeni.organization_tenancy_lifecycle', true);
|
|
23
|
+
permissions_value jsonb;
|
|
24
|
+
member_id uuid;
|
|
25
|
+
BEGIN
|
|
26
|
+
IF p_account IS NULL OR p_actor IS NULL
|
|
27
|
+
OR p_account IS DISTINCT FROM opengeni_private.current_account_id()
|
|
28
|
+
OR p_actor IS DISTINCT FROM opengeni_private.current_subject_id() THEN
|
|
29
|
+
RAISE EXCEPTION 'organization administration scope invalid' USING ERRCODE = '42501';
|
|
30
|
+
END IF;
|
|
31
|
+
IF p_actor LIKE 'api_key:%' THEN
|
|
32
|
+
SELECT permissions INTO permissions_value FROM api_keys
|
|
33
|
+
WHERE account_id = p_account AND 'api_key:' || id::text = p_actor
|
|
34
|
+
AND credential_kind = 'organization' AND workspace_id IS NULL AND revoked_at IS NULL
|
|
35
|
+
AND (expires_at IS NULL OR expires_at > clock_timestamp()) FOR SHARE;
|
|
36
|
+
IF NOT coalesce(permissions_value ? 'workspace:admin', false) THEN
|
|
37
|
+
RAISE EXCEPTION 'organization administrator required' USING ERRCODE = '42501';
|
|
38
|
+
END IF;
|
|
39
|
+
ELSE
|
|
40
|
+
PERFORM set_config('opengeni.organization_tenancy_lifecycle', 'organization_private_session_settings', true);
|
|
41
|
+
SELECT id INTO member_id FROM organization_memberships
|
|
42
|
+
WHERE account_id = p_account AND subject_id = p_actor AND status = 'active' AND role IN ('owner','admin') FOR SHARE;
|
|
43
|
+
PERFORM set_config('opengeni.organization_tenancy_lifecycle', coalesce(previous_marker, ''), true);
|
|
44
|
+
IF member_id IS NULL THEN RAISE EXCEPTION 'organization administrator required' USING ERRCODE = '42501'; END IF;
|
|
45
|
+
END IF;
|
|
46
|
+
END $body$;
|
|
47
|
+
REVOKE ALL ON FUNCTION opengeni_private.assert_organization_administrator(uuid,text) FROM PUBLIC;
|
|
48
|
+
-- Replace in place to preserve existing runtime EXECUTE grants during rollout.
|
|
49
|
+
CREATE OR REPLACE FUNCTION opengeni_private.assert_organization_integration_policy_administrator(
|
|
50
|
+
p_account uuid, p_actor text
|
|
51
|
+
) RETURNS void LANGUAGE sql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
52
|
+
SELECT opengeni_private.assert_organization_administrator(p_account, p_actor)
|
|
53
|
+
$body$;
|
|
54
|
+
|
|
55
|
+
CREATE OR REPLACE FUNCTION get_organization_private_session_settings(
|
|
56
|
+
p_account_id uuid, p_actor_subject_id text
|
|
57
|
+
) RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
58
|
+
DECLARE
|
|
59
|
+
setting organization_private_session_settings%ROWTYPE;
|
|
60
|
+
account_created_at timestamptz;
|
|
61
|
+
BEGIN
|
|
62
|
+
PERFORM opengeni_private.assert_organization_administrator(p_account_id, p_actor_subject_id);
|
|
63
|
+
PERFORM set_config('opengeni.organization_tenancy_lifecycle', 'organization_private_session_settings', true);
|
|
64
|
+
SELECT created_at INTO account_created_at FROM managed_accounts WHERE id = p_account_id;
|
|
65
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'organization not found' USING ERRCODE = 'P0002'; END IF;
|
|
66
|
+
SELECT * INTO setting FROM organization_private_session_settings WHERE account_id = p_account_id;
|
|
67
|
+
RETURN jsonb_build_object(
|
|
68
|
+
'organizationId', p_account_id,
|
|
69
|
+
'enabled', coalesce(setting.enabled, false),
|
|
70
|
+
'available', session_tenancy_product_activated(p_account_id, 1),
|
|
71
|
+
'version', coalesce(setting.version, 0),
|
|
72
|
+
'updatedAt', coalesce(setting.updated_at, account_created_at)
|
|
73
|
+
);
|
|
74
|
+
END $body$;
|
|
75
|
+
|
|
76
|
+
CREATE OR REPLACE FUNCTION update_organization_private_session_settings(
|
|
77
|
+
p_account_id uuid, p_actor_subject_id text, p_enabled boolean,
|
|
78
|
+
p_expected_version bigint, p_operation_id uuid
|
|
79
|
+
) RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $body$
|
|
80
|
+
DECLARE
|
|
81
|
+
actor_id uuid;
|
|
82
|
+
setting organization_private_session_settings%ROWTYPE;
|
|
83
|
+
prior organization_private_session_setting_events%ROWTYPE;
|
|
84
|
+
next_version bigint;
|
|
85
|
+
did_change boolean;
|
|
86
|
+
BEGIN
|
|
87
|
+
IF p_account_id IS NULL OR p_enabled IS NULL OR p_operation_id IS NULL
|
|
88
|
+
OR p_expected_version IS NULL OR p_expected_version < 0
|
|
89
|
+
OR p_actor_subject_id IS DISTINCT FROM opengeni_private.current_subject_id()
|
|
90
|
+
OR p_account_id IS DISTINCT FROM opengeni_private.current_account_id()
|
|
91
|
+
THEN
|
|
92
|
+
RAISE EXCEPTION 'organization private-session settings request is invalid' USING ERRCODE = '22023';
|
|
93
|
+
END IF;
|
|
94
|
+
PERFORM pg_advisory_xact_lock(hashtextextended('organization-membership:' || p_account_id::text, 0));
|
|
95
|
+
PERFORM set_config('opengeni.organization_tenancy_lifecycle', 'organization_private_session_settings', true);
|
|
96
|
+
PERFORM 1 FROM managed_accounts WHERE id = p_account_id FOR KEY SHARE;
|
|
97
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'organization not found' USING ERRCODE = 'P0002'; END IF;
|
|
98
|
+
-- Revoked/expired keys cannot replay old successful commands.
|
|
99
|
+
PERFORM opengeni_private.assert_organization_administrator(p_account_id, p_actor_subject_id);
|
|
100
|
+
SELECT id INTO actor_id FROM organization_memberships
|
|
101
|
+
WHERE account_id = p_account_id AND subject_id = p_actor_subject_id;
|
|
102
|
+
SELECT * INTO prior FROM organization_private_session_setting_events WHERE id = p_operation_id;
|
|
103
|
+
IF FOUND THEN
|
|
104
|
+
IF prior.account_id IS DISTINCT FROM p_account_id
|
|
105
|
+
OR prior.actor_membership_id IS DISTINCT FROM actor_id
|
|
106
|
+
OR (prior.actor_subject_id IS NOT NULL AND prior.actor_subject_id IS DISTINCT FROM p_actor_subject_id)
|
|
107
|
+
OR prior.requested_enabled IS DISTINCT FROM p_enabled
|
|
108
|
+
OR prior.expected_version IS DISTINCT FROM p_expected_version
|
|
109
|
+
THEN
|
|
110
|
+
RAISE EXCEPTION 'organization private-session operation key was reused' USING ERRCODE = '23505';
|
|
111
|
+
END IF;
|
|
112
|
+
RETURN jsonb_build_object(
|
|
113
|
+
'organizationId', p_account_id, 'enabled', prior.result_enabled,
|
|
114
|
+
'available', session_tenancy_product_activated(p_account_id, 1),
|
|
115
|
+
'version', prior.result_version, 'updatedAt', prior.result_updated_at, 'changed', prior.changed
|
|
116
|
+
);
|
|
117
|
+
END IF;
|
|
118
|
+
SELECT * INTO setting FROM organization_private_session_settings WHERE account_id = p_account_id FOR UPDATE;
|
|
119
|
+
IF coalesce(setting.version, 0) IS DISTINCT FROM p_expected_version THEN
|
|
120
|
+
RAISE EXCEPTION 'organization private-session settings changed before update' USING ERRCODE = '40001';
|
|
121
|
+
END IF;
|
|
122
|
+
IF p_enabled AND NOT session_tenancy_product_activated(p_account_id, 1) THEN
|
|
123
|
+
RAISE EXCEPTION 'session tenancy product surface is not available for this organization' USING ERRCODE = '55000';
|
|
124
|
+
END IF;
|
|
125
|
+
did_change := coalesce(setting.enabled, false) IS DISTINCT FROM p_enabled;
|
|
126
|
+
next_version := CASE WHEN setting.account_id IS NULL THEN 1 ELSE setting.version + 1 END;
|
|
127
|
+
INSERT INTO organization_private_session_settings (
|
|
128
|
+
account_id, enabled, version, updated_by_membership_id, updated_at
|
|
129
|
+
) VALUES (p_account_id, p_enabled, next_version, actor_id, clock_timestamp())
|
|
130
|
+
ON CONFLICT (account_id) DO UPDATE SET
|
|
131
|
+
enabled = excluded.enabled, version = excluded.version,
|
|
132
|
+
updated_by_membership_id = excluded.updated_by_membership_id, updated_at = excluded.updated_at
|
|
133
|
+
RETURNING * INTO setting;
|
|
134
|
+
INSERT INTO organization_private_session_setting_events (
|
|
135
|
+
id, account_id, actor_membership_id, actor_subject_id, requested_enabled,
|
|
136
|
+
expected_version, result_enabled, result_version, result_updated_at, changed
|
|
137
|
+
) VALUES (
|
|
138
|
+
p_operation_id, p_account_id, actor_id, p_actor_subject_id, p_enabled,
|
|
139
|
+
p_expected_version, setting.enabled, setting.version, setting.updated_at, did_change
|
|
140
|
+
);
|
|
141
|
+
RETURN jsonb_build_object(
|
|
142
|
+
'organizationId', p_account_id, 'enabled', setting.enabled,
|
|
143
|
+
'available', session_tenancy_product_activated(p_account_id, 1),
|
|
144
|
+
'version', setting.version, 'updatedAt', setting.updated_at, 'changed', did_change
|
|
145
|
+
);
|
|
146
|
+
END $body$;
|
|
147
|
+
|
|
148
|
+
DO $safe_path$
|
|
149
|
+
BEGIN
|
|
150
|
+
EXECUTE format('ALTER FUNCTION opengeni_private.assert_organization_administrator(uuid,text) SET search_path = pg_catalog, %I, pg_temp', current_schema());
|
|
151
|
+
EXECUTE format('ALTER FUNCTION opengeni_private.assert_organization_integration_policy_administrator(uuid,text) SET search_path = pg_catalog, %I, pg_temp', current_schema());
|
|
152
|
+
EXECUTE format('ALTER FUNCTION get_organization_private_session_settings(uuid,text) SET search_path = pg_catalog, %I, pg_temp', current_schema());
|
|
153
|
+
EXECUTE format('ALTER FUNCTION update_organization_private_session_settings(uuid,text,boolean,bigint,uuid) SET search_path = pg_catalog, %I, pg_temp', current_schema());
|
|
154
|
+
END $safe_path$;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Visibility changes invalidate session-initial personal connection selections.
|
|
3
|
+
-- Permit that exact cleanup under the existing transaction-local lifecycle
|
|
4
|
+
-- capability; accepted turn authority and parent provenance remain immutable.
|
|
5
|
+
SET LOCAL lock_timeout = '5s';
|
|
6
|
+
SET LOCAL statement_timeout = '5min';
|
|
7
|
+
|
|
8
|
+
CREATE OR REPLACE FUNCTION opengeni_private.prevent_personal_connection_authority_mutation()
|
|
9
|
+
RETURNS trigger LANGUAGE plpgsql SET search_path FROM CURRENT AS $body$
|
|
10
|
+
BEGIN
|
|
11
|
+
IF TG_TABLE_NAME = 'sessions' THEN
|
|
12
|
+
IF NEW.parent_turn_id IS DISTINCT FROM OLD.parent_turn_id THEN
|
|
13
|
+
RAISE EXCEPTION 'session parent turn authority is immutable' USING ERRCODE = '23514';
|
|
14
|
+
END IF;
|
|
15
|
+
IF NEW.initial_personal_connection_delegations IS DISTINCT FROM OLD.initial_personal_connection_delegations THEN
|
|
16
|
+
IF NEW.initial_personal_connection_delegations IS DISTINCT FROM '[]'::jsonb
|
|
17
|
+
OR NEW.visibility IS NOT DISTINCT FROM OLD.visibility
|
|
18
|
+
OR NEW.authority_epoch IS DISTINCT FROM OLD.authority_epoch + 1
|
|
19
|
+
OR NEW.owner_subject_id IS DISTINCT FROM OLD.owner_subject_id
|
|
20
|
+
OR NEW.owner_organization_membership_id IS DISTINCT FROM OLD.owner_organization_membership_id
|
|
21
|
+
THEN
|
|
22
|
+
RAISE EXCEPTION 'session initial personal MCP authority is immutable' USING ERRCODE = '23514';
|
|
23
|
+
END IF;
|
|
24
|
+
-- A caller-set GUC is insufficient: only the existing protected capability
|
|
25
|
+
-- row minted by the native visibility transition permits this cleanup.
|
|
26
|
+
IF NOT EXISTS (
|
|
27
|
+
SELECT 1 FROM session_visibility_write_capabilities capability
|
|
28
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
29
|
+
AND capability.transaction_id = pg_current_xact_id()
|
|
30
|
+
AND capability.capability_id = nullif(current_setting(
|
|
31
|
+
'opengeni.session_visibility_write_capability', true
|
|
32
|
+
), '')::uuid
|
|
33
|
+
) THEN
|
|
34
|
+
RAISE EXCEPTION 'session personal authority cleanup requires the visibility lifecycle capability' USING ERRCODE = '42501';
|
|
35
|
+
END IF;
|
|
36
|
+
END IF;
|
|
37
|
+
ELSIF NEW.personal_connection_delegations IS DISTINCT FROM OLD.personal_connection_delegations THEN
|
|
38
|
+
RAISE EXCEPTION '% personal MCP authority is immutable', TG_TABLE_NAME USING ERRCODE = '23514';
|
|
39
|
+
END IF;
|
|
40
|
+
RETURN NEW;
|
|
41
|
+
END $body$;
|
|
42
|
+
DO $safe_path$
|
|
43
|
+
BEGIN
|
|
44
|
+
EXECUTE format('ALTER FUNCTION opengeni_private.prevent_personal_connection_authority_mutation() SET search_path = pg_catalog, %I, pg_temp', current_schema());
|
|
45
|
+
END $safe_path$;
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
-- deployment-mode: maintenance
|
|
2
|
+
-- Destructive feature removal. Drain all API/control/turn workers before this
|
|
3
|
+
-- cutover and start only matching binaries. No Pack data is migrated forward.
|
|
4
|
+
SET LOCAL lock_timeout = '5s';
|
|
5
|
+
SET LOCAL statement_timeout = '10min';
|
|
6
|
+
|
|
7
|
+
DO $drain$
|
|
8
|
+
DECLARE roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
|
|
9
|
+
BEGIN
|
|
10
|
+
IF roles IS NULL OR jsonb_typeof(roles) <> 'array' OR jsonb_array_length(roles) NOT BETWEEN 1 AND 16
|
|
11
|
+
OR EXISTS (SELECT 1 FROM jsonb_array_elements(roles) r WHERE jsonb_typeof(r) <> 'string' OR length(btrim(r #>> '{}')) NOT BETWEEN 1 AND 63)
|
|
12
|
+
THEN RAISE EXCEPTION 'Pack removal requires explicit application database roles' USING ERRCODE='55000'; END IF;
|
|
13
|
+
IF EXISTS (SELECT 1 FROM pg_stat_activity a JOIN jsonb_array_elements_text(roles) r ON a.usename=r.value
|
|
14
|
+
WHERE a.datname=current_database() AND a.pid<>pg_backend_pid())
|
|
15
|
+
THEN RAISE EXCEPTION 'Pack removal requires drained application sessions' USING ERRCODE='55000'; END IF;
|
|
16
|
+
END $drain$;
|
|
17
|
+
|
|
18
|
+
-- The migration owner is NOSUPERUSER/NOBYPASSRLS. Temporarily lift FORCE,
|
|
19
|
+
-- never policies, only while the drained owner performs this atomic cutover.
|
|
20
|
+
LOCK TABLE workspace_packs, pack_installations, pack_installation_components,
|
|
21
|
+
capability_operations, capability_component_owners, capability_facet_installations,
|
|
22
|
+
capability_plugin_installations, integration_facet_binding_owners, integration_facet_bindings,
|
|
23
|
+
skill_source_bindings, preference_registry_preferences, preference_registry_revisions,
|
|
24
|
+
preference_registry_events, automation_sources, automation_triggers, automation_runs,
|
|
25
|
+
automation_trigger_revisions, automation_trigger_events, automation_run_event_links,
|
|
26
|
+
automation_webhook_endpoints
|
|
27
|
+
IN ACCESS EXCLUSIVE MODE;
|
|
28
|
+
ALTER TABLE workspace_packs NO FORCE ROW LEVEL SECURITY;
|
|
29
|
+
ALTER TABLE pack_installations NO FORCE ROW LEVEL SECURITY;
|
|
30
|
+
ALTER TABLE pack_installation_components NO FORCE ROW LEVEL SECURITY;
|
|
31
|
+
ALTER TABLE capability_operations NO FORCE ROW LEVEL SECURITY;
|
|
32
|
+
ALTER TABLE capability_component_owners NO FORCE ROW LEVEL SECURITY;
|
|
33
|
+
ALTER TABLE capability_facet_installations NO FORCE ROW LEVEL SECURITY;
|
|
34
|
+
ALTER TABLE capability_plugin_installations NO FORCE ROW LEVEL SECURITY;
|
|
35
|
+
ALTER TABLE integration_facet_binding_owners NO FORCE ROW LEVEL SECURITY;
|
|
36
|
+
ALTER TABLE integration_facet_bindings NO FORCE ROW LEVEL SECURITY;
|
|
37
|
+
ALTER TABLE skill_source_bindings NO FORCE ROW LEVEL SECURITY;
|
|
38
|
+
ALTER TABLE preference_registry_preferences NO FORCE ROW LEVEL SECURITY;
|
|
39
|
+
ALTER TABLE preference_registry_revisions NO FORCE ROW LEVEL SECURITY;
|
|
40
|
+
ALTER TABLE preference_registry_events NO FORCE ROW LEVEL SECURITY;
|
|
41
|
+
ALTER TABLE automation_sources NO FORCE ROW LEVEL SECURITY;
|
|
42
|
+
ALTER TABLE automation_triggers NO FORCE ROW LEVEL SECURITY;
|
|
43
|
+
ALTER TABLE automation_runs NO FORCE ROW LEVEL SECURITY;
|
|
44
|
+
ALTER TABLE automation_trigger_revisions NO FORCE ROW LEVEL SECURITY;
|
|
45
|
+
ALTER TABLE automation_trigger_events NO FORCE ROW LEVEL SECURITY;
|
|
46
|
+
ALTER TABLE automation_run_event_links NO FORCE ROW LEVEL SECURITY;
|
|
47
|
+
|
|
48
|
+
-- Capture only installations whose last owner is the removed feature. Shared
|
|
49
|
+
-- direct, Plugin and migration ownership remains untouched.
|
|
50
|
+
CREATE TEMP TABLE removed_pack_facets ON COMMIT DROP AS
|
|
51
|
+
SELECT fi.* FROM capability_facet_installations fi
|
|
52
|
+
WHERE EXISTS (SELECT 1 FROM capability_component_owners o WHERE o.facet_installation_id=fi.id AND o.owner_kind='pack')
|
|
53
|
+
AND NOT EXISTS (SELECT 1 FROM capability_component_owners o WHERE o.facet_installation_id=fi.id AND o.owner_kind<>'pack');
|
|
54
|
+
CREATE TEMP TABLE removed_pack_bindings ON COMMIT DROP AS
|
|
55
|
+
SELECT binding.id FROM integration_facet_bindings binding
|
|
56
|
+
WHERE EXISTS (SELECT 1 FROM integration_facet_binding_owners o WHERE o.binding_id=binding.id AND o.owner_kind='pack')
|
|
57
|
+
AND NOT EXISTS (SELECT 1 FROM integration_facet_binding_owners o WHERE o.binding_id=binding.id AND o.owner_kind<>'pack');
|
|
58
|
+
CREATE TEMP TABLE removed_pack_sources ON COMMIT DROP AS
|
|
59
|
+
SELECT id FROM automation_sources
|
|
60
|
+
WHERE pack_installation_id IS NOT NULL AND adapter_id<>'source-control.pull-request.v1';
|
|
61
|
+
CREATE TEMP TABLE removed_pack_triggers ON COMMIT DROP AS
|
|
62
|
+
SELECT trigger.id FROM automation_triggers trigger JOIN automation_sources source ON source.id=trigger.source_id
|
|
63
|
+
WHERE source.adapter_id<>'source-control.pull-request.v1'
|
|
64
|
+
AND (trigger.pack_installation_id IS NOT NULL OR source.id IN (SELECT id FROM removed_pack_sources));
|
|
65
|
+
CREATE TEMP TABLE removed_pack_events ON COMMIT DROP AS
|
|
66
|
+
SELECT event.id FROM automation_trigger_events event
|
|
67
|
+
WHERE event.source_id IN (SELECT id FROM removed_pack_sources)
|
|
68
|
+
OR EXISTS (SELECT 1 FROM jsonb_array_elements(event.matched_trigger_revisions) matched
|
|
69
|
+
JOIN removed_pack_triggers removed ON removed.id::text=matched->>'triggerId');
|
|
70
|
+
|
|
71
|
+
DO $preflight$
|
|
72
|
+
BEGIN
|
|
73
|
+
-- Historical rows can violate the former application-only source/trigger
|
|
74
|
+
-- ownership invariant. Remove exclusively Pack events, but never erase an
|
|
75
|
+
-- event that also records independent work. Refuse that ambiguous cutover.
|
|
76
|
+
IF EXISTS (SELECT 1 FROM automation_trigger_events event
|
|
77
|
+
WHERE event.id IN (SELECT id FROM removed_pack_events) AND (
|
|
78
|
+
EXISTS (SELECT 1 FROM jsonb_array_elements(event.matched_trigger_revisions) matched
|
|
79
|
+
WHERE NOT EXISTS (SELECT 1 FROM removed_pack_triggers removed WHERE removed.id::text=matched->>'triggerId'))
|
|
80
|
+
OR EXISTS (SELECT 1 FROM automation_runs run WHERE run.event_id=event.id
|
|
81
|
+
AND run.trigger_id NOT IN (SELECT id FROM removed_pack_triggers))
|
|
82
|
+
OR EXISTS (SELECT 1 FROM automation_run_event_links link JOIN automation_runs run ON run.id=link.run_id
|
|
83
|
+
WHERE link.event_id=event.id AND run.trigger_id NOT IN (SELECT id FROM removed_pack_triggers))))
|
|
84
|
+
THEN RAISE EXCEPTION 'Resolve mixed Pack and independent automation events before removal' USING ERRCODE='55000'; END IF;
|
|
85
|
+
IF EXISTS (SELECT 1 FROM capability_operations
|
|
86
|
+
WHERE status IN ('pending','running','outcome_unknown')
|
|
87
|
+
AND (target_kind='pack' OR (target_kind='facet_binding' AND target_id IN (SELECT id::text FROM removed_pack_bindings))))
|
|
88
|
+
THEN RAISE EXCEPTION 'Settle Pack-related capability operations before Pack removal' USING ERRCODE='55000'; END IF;
|
|
89
|
+
IF EXISTS (SELECT 1 FROM automation_runs
|
|
90
|
+
WHERE (source_id IN (SELECT id FROM removed_pack_sources) OR trigger_id IN (SELECT id FROM removed_pack_triggers))
|
|
91
|
+
AND status IN ('queued','dispatching'))
|
|
92
|
+
THEN RAISE EXCEPTION 'Settle Pack automation runs before Pack removal' USING ERRCODE='55000'; END IF;
|
|
93
|
+
END $preflight$;
|
|
94
|
+
|
|
95
|
+
-- This is maintenance deactivation, not an invented human approval. Keep all
|
|
96
|
+
-- immutable Skill revisions/events and preserve customized/re-scoped heads.
|
|
97
|
+
DO $release_skills$
|
|
98
|
+
DECLARE head record; next_event integer;
|
|
99
|
+
BEGIN
|
|
100
|
+
FOR head IN
|
|
101
|
+
SELECT DISTINCT p.* FROM preference_registry_preferences p
|
|
102
|
+
JOIN preference_registry_revisions r ON r.id=p.active_revision_id AND r.preference_id=p.id
|
|
103
|
+
JOIN skill_source_bindings b ON b.preference_id=p.id AND b.account_id=p.account_id
|
|
104
|
+
JOIN removed_pack_facets fi ON fi.facet_id=b.skill_facet_id AND fi.workspace_id=b.workspace_id AND fi.account_id=b.account_id
|
|
105
|
+
WHERE p.status='active' AND r.provenance_source='portable_skill'
|
|
106
|
+
AND p.scope='workspace' AND p.scope_workspace_id=b.workspace_id
|
|
107
|
+
AND NOT EXISTS (
|
|
108
|
+
SELECT 1 FROM skill_source_bindings other
|
|
109
|
+
JOIN capability_facet_installations installed ON installed.facet_id=other.skill_facet_id
|
|
110
|
+
AND installed.account_id=other.account_id AND installed.workspace_id=other.workspace_id
|
|
111
|
+
JOIN capability_component_owners owner ON owner.facet_installation_id=installed.id
|
|
112
|
+
WHERE other.preference_id=p.id AND owner.owner_kind<>'pack'
|
|
113
|
+
)
|
|
114
|
+
ORDER BY p.id
|
|
115
|
+
LOOP
|
|
116
|
+
PERFORM set_config('opengeni.preference_lifecycle_head_id',head.id::text,true);
|
|
117
|
+
PERFORM set_config('opengeni.preference_lifecycle_operation','deactivate',true);
|
|
118
|
+
UPDATE preference_registry_preferences SET status='inactive',active_revision_id=NULL,
|
|
119
|
+
active_revision=NULL,active_content_hash=NULL,activation_version=activation_version+1,updated_at=clock_timestamp()
|
|
120
|
+
WHERE id=head.id;
|
|
121
|
+
SELECT coalesce(max(version),0)+1 INTO next_event FROM preference_registry_events WHERE preference_id=head.id;
|
|
122
|
+
INSERT INTO preference_registry_events(account_id,preference_id,type,version,old_revision_id,actor_subject_id,reason)
|
|
123
|
+
VALUES(head.account_id,head.id,'deactivated',next_event,head.active_revision_id,
|
|
124
|
+
'service:remove-packs:0482','Removed the last distribution owner during Pack removal');
|
|
125
|
+
END LOOP;
|
|
126
|
+
END $release_skills$;
|
|
127
|
+
|
|
128
|
+
DELETE FROM integration_facet_binding_owners WHERE owner_kind='pack';
|
|
129
|
+
DELETE FROM capability_component_owners WHERE owner_kind='pack';
|
|
130
|
+
DELETE FROM integration_facet_bindings WHERE id IN (SELECT id FROM removed_pack_bindings);
|
|
131
|
+
DELETE FROM skill_source_bindings binding USING removed_pack_facets removed
|
|
132
|
+
WHERE binding.skill_facet_id=removed.facet_id AND binding.account_id=removed.account_id
|
|
133
|
+
AND binding.workspace_id=removed.workspace_id
|
|
134
|
+
AND NOT EXISTS (SELECT 1 FROM capability_facet_installations surviving
|
|
135
|
+
WHERE surviving.facet_id=removed.facet_id AND surviving.account_id=removed.account_id
|
|
136
|
+
AND surviving.workspace_id=removed.workspace_id AND surviving.id NOT IN (SELECT id FROM removed_pack_facets));
|
|
137
|
+
DELETE FROM capability_facet_installations WHERE id IN (SELECT id FROM removed_pack_facets);
|
|
138
|
+
DELETE FROM capability_plugin_installations installation
|
|
139
|
+
WHERE installation.id IN (SELECT plugin_installation_id FROM removed_pack_facets)
|
|
140
|
+
AND NOT EXISTS (SELECT 1 FROM capability_facet_installations fi WHERE fi.plugin_installation_id=installation.id)
|
|
141
|
+
AND NOT EXISTS (SELECT 1 FROM capability_component_owners owner WHERE owner.owner_kind='plugin' AND owner.owner_id=installation.id::text)
|
|
142
|
+
AND NOT EXISTS (SELECT 1 FROM integration_facet_binding_owners owner WHERE owner.owner_kind='plugin' AND owner.owner_id=installation.id::text);
|
|
143
|
+
|
|
144
|
+
-- PR Review already uses the independent source/trigger/registration system.
|
|
145
|
+
-- Keep its IDs, credentials, revisions and history; remove only the obsolete
|
|
146
|
+
-- ownership columns. Other Pack automations are removed, not converted.
|
|
147
|
+
-- Remove completed Pack execution records in FK order. Sessions referenced by
|
|
148
|
+
-- those records are independent history and are never deleted here.
|
|
149
|
+
DELETE FROM automation_run_event_links WHERE run_id IN (
|
|
150
|
+
SELECT id FROM automation_runs WHERE source_id IN (SELECT id FROM removed_pack_sources)
|
|
151
|
+
OR trigger_id IN (SELECT id FROM removed_pack_triggers)
|
|
152
|
+
) OR event_id IN (SELECT id FROM removed_pack_events);
|
|
153
|
+
DELETE FROM automation_runs WHERE source_id IN (SELECT id FROM removed_pack_sources)
|
|
154
|
+
OR trigger_id IN (SELECT id FROM removed_pack_triggers);
|
|
155
|
+
DELETE FROM automation_trigger_events WHERE id IN (SELECT id FROM removed_pack_events);
|
|
156
|
+
DELETE FROM automation_trigger_revisions WHERE trigger_id IN (SELECT id FROM removed_pack_triggers);
|
|
157
|
+
DELETE FROM automation_triggers WHERE id IN (SELECT id FROM removed_pack_triggers);
|
|
158
|
+
DELETE FROM automation_webhook_endpoints WHERE source_id IN (SELECT id FROM removed_pack_sources);
|
|
159
|
+
DELETE FROM automation_sources WHERE id IN (SELECT id FROM removed_pack_sources);
|
|
160
|
+
ALTER TABLE automation_sources DROP CONSTRAINT automation_sources_pack_installation_fk;
|
|
161
|
+
ALTER TABLE automation_sources DROP CONSTRAINT automation_sources_shape_chk;
|
|
162
|
+
ALTER TABLE automation_sources DROP COLUMN pack_installation_id, DROP COLUMN pack_connector_id;
|
|
163
|
+
ALTER TABLE automation_sources ADD CONSTRAINT automation_sources_shape_chk CHECK (
|
|
164
|
+
status IN ('active','disabled') AND version>0 AND octet_length(name) BETWEEN 1 AND 512
|
|
165
|
+
AND octet_length(adapter_id) BETWEEN 1 AND 128 AND octet_length(created_by_subject_id) BETWEEN 1 AND 4096
|
|
166
|
+
AND jsonb_typeof(configuration)='object'
|
|
167
|
+
);
|
|
168
|
+
ALTER TABLE automation_triggers DROP CONSTRAINT automation_triggers_pack_installation_fk;
|
|
169
|
+
ALTER TABLE automation_triggers DROP CONSTRAINT automation_triggers_shape_chk;
|
|
170
|
+
ALTER TABLE automation_triggers DROP COLUMN pack_installation_id, DROP COLUMN pack_template_id;
|
|
171
|
+
ALTER TABLE automation_triggers ADD CONSTRAINT automation_triggers_shape_chk CHECK (
|
|
172
|
+
status IN ('active','paused','disabled') AND current_revision>0
|
|
173
|
+
AND octet_length(name) BETWEEN 1 AND 512 AND octet_length(created_by_subject_id) BETWEEN 1 AND 4096
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
DELETE FROM capability_operations WHERE target_kind='pack';
|
|
177
|
+
ALTER TABLE capability_component_owners DROP CONSTRAINT capability_component_owners_kind_chk;
|
|
178
|
+
ALTER TABLE capability_component_owners ADD CONSTRAINT capability_component_owners_kind_chk CHECK (owner_kind IN ('direct','plugin','migration'));
|
|
179
|
+
ALTER TABLE integration_facet_binding_owners DROP CONSTRAINT integration_facet_binding_owners_kind_chk;
|
|
180
|
+
ALTER TABLE integration_facet_binding_owners ADD CONSTRAINT integration_facet_binding_owners_kind_chk CHECK (owner_kind IN ('direct','plugin','migration'));
|
|
181
|
+
ALTER TABLE capability_operations DROP CONSTRAINT capability_operations_target_chk;
|
|
182
|
+
ALTER TABLE capability_operations ADD CONSTRAINT capability_operations_target_chk CHECK (
|
|
183
|
+
target_kind IN ('plugin','integration','skill','facet_binding') AND length(target_id) BETWEEN 1 AND 512
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
-- Function definitions below retain Plugin finalization and tenant validation.
|
|
187
|
+
-- They are replaced before dropping tables so no active SQL body retains Pack
|
|
188
|
+
-- lookups. DROP without CASCADE refuses unaccounted dependencies.
|
|
189
|
+
CREATE OR REPLACE FUNCTION capability_v2_validate_component_owner()
|
|
190
|
+
RETURNS trigger
|
|
191
|
+
LANGUAGE plpgsql
|
|
192
|
+
AS $$
|
|
193
|
+
BEGIN
|
|
194
|
+
IF NOT EXISTS (
|
|
195
|
+
SELECT 1 FROM "capability_facet_installations" i
|
|
196
|
+
WHERE i."id" = NEW."facet_installation_id"
|
|
197
|
+
AND i."account_id" = NEW."account_id"
|
|
198
|
+
AND i."workspace_id" = NEW."workspace_id"
|
|
199
|
+
) THEN
|
|
200
|
+
RAISE EXCEPTION 'component owner does not match its facet installation tenant'
|
|
201
|
+
USING ERRCODE = '23514';
|
|
202
|
+
END IF;
|
|
203
|
+
IF NEW."owner_kind" = 'plugin' THEN
|
|
204
|
+
IF NEW."owner_id" !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$' THEN
|
|
205
|
+
RAISE EXCEPTION 'plugin component owner belongs to another tenant or does not exist'
|
|
206
|
+
USING ERRCODE = '23514';
|
|
207
|
+
ELSIF NOT EXISTS (
|
|
208
|
+
SELECT 1 FROM "capability_plugin_installations" p
|
|
209
|
+
WHERE p."id" = NEW."owner_id"::uuid
|
|
210
|
+
AND p."account_id" = NEW."account_id"
|
|
211
|
+
AND p."workspace_id" = NEW."workspace_id"
|
|
212
|
+
) THEN
|
|
213
|
+
RAISE EXCEPTION 'plugin component owner belongs to another tenant or does not exist'
|
|
214
|
+
USING ERRCODE = '23514';
|
|
215
|
+
END IF;
|
|
216
|
+
END IF;
|
|
217
|
+
RETURN NEW;
|
|
218
|
+
END;
|
|
219
|
+
$$;
|
|
220
|
+
|
|
221
|
+
CREATE OR REPLACE FUNCTION capability_v2_validate_facet_binding_owner()
|
|
222
|
+
RETURNS trigger
|
|
223
|
+
LANGUAGE plpgsql
|
|
224
|
+
AS $$
|
|
225
|
+
BEGIN
|
|
226
|
+
IF NOT EXISTS (
|
|
227
|
+
SELECT 1 FROM integration_facet_bindings binding
|
|
228
|
+
WHERE binding.id = NEW.binding_id
|
|
229
|
+
AND binding.account_id = NEW.account_id
|
|
230
|
+
AND binding.workspace_id = NEW.workspace_id
|
|
231
|
+
) THEN
|
|
232
|
+
RAISE EXCEPTION 'Facet binding owner does not match its binding tenant'
|
|
233
|
+
USING ERRCODE = '23514';
|
|
234
|
+
END IF;
|
|
235
|
+
IF NEW.owner_kind = 'plugin' THEN
|
|
236
|
+
IF NEW.owner_id !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$' THEN
|
|
237
|
+
RAISE EXCEPTION 'Plugin Facet binding owner belongs to another tenant or does not exist'
|
|
238
|
+
USING ERRCODE = '23514';
|
|
239
|
+
ELSIF NOT EXISTS (
|
|
240
|
+
SELECT 1 FROM capability_plugin_installations plugin
|
|
241
|
+
WHERE plugin.id = NEW.owner_id::uuid
|
|
242
|
+
AND plugin.account_id = NEW.account_id
|
|
243
|
+
AND plugin.workspace_id = NEW.workspace_id
|
|
244
|
+
) THEN
|
|
245
|
+
RAISE EXCEPTION 'Plugin Facet binding owner belongs to another tenant or does not exist'
|
|
246
|
+
USING ERRCODE = '23514';
|
|
247
|
+
END IF;
|
|
248
|
+
END IF;
|
|
249
|
+
RETURN NEW;
|
|
250
|
+
END;
|
|
251
|
+
$$;
|
|
252
|
+
|
|
253
|
+
CREATE OR REPLACE FUNCTION skill_source_has_effective_owner(p_account_id uuid,p_workspace_id uuid,p_facet_id uuid)
|
|
254
|
+
RETURNS boolean LANGUAGE sql STABLE SET search_path FROM CURRENT AS $$
|
|
255
|
+
SELECT EXISTS (
|
|
256
|
+
SELECT 1 FROM capability_facet_installations fi
|
|
257
|
+
JOIN capability_plugin_installations child ON child.id=fi.plugin_installation_id
|
|
258
|
+
JOIN capability_component_owners owner ON owner.facet_installation_id=fi.id
|
|
259
|
+
AND owner.account_id=fi.account_id AND owner.workspace_id=fi.workspace_id
|
|
260
|
+
WHERE fi.account_id=p_account_id AND fi.workspace_id=p_workspace_id AND fi.facet_id=p_facet_id
|
|
261
|
+
AND fi.status='active' AND child.status='active'
|
|
262
|
+
AND (owner.owner_kind IN ('direct','migration')
|
|
263
|
+
OR (owner.owner_kind='plugin' AND EXISTS(SELECT 1 FROM capability_plugin_installations parent
|
|
264
|
+
WHERE parent.id::text=owner.owner_id AND parent.account_id=p_account_id AND parent.workspace_id=p_workspace_id AND parent.status='active')))
|
|
265
|
+
)
|
|
266
|
+
$$;
|
|
267
|
+
|
|
268
|
+
CREATE OR REPLACE FUNCTION skill_publish_finalized_owner() RETURNS trigger
|
|
269
|
+
LANGUAGE plpgsql SECURITY DEFINER SET search_path FROM CURRENT AS $$
|
|
270
|
+
DECLARE
|
|
271
|
+
publication_owner_kind text := 'plugin';
|
|
272
|
+
pending record; head preference_registry_preferences%ROWTYPE; revision preference_registry_revisions%ROWTYPE;
|
|
273
|
+
publication_id uuid; publication_hash text; publication_operation uuid; expected_fingerprint text; event_id uuid;
|
|
274
|
+
next_event integer; result jsonb; disposition text; current_mode text; parent_target text;
|
|
275
|
+
publication_actor jsonb := jsonb_build_object('kind','service','subjectId','service:skill-publication','principalKind','service');
|
|
276
|
+
BEGIN
|
|
277
|
+
PERFORM pg_advisory_xact_lock(hashtextextended('skill-publication:'||NEW.workspace_id,0));
|
|
278
|
+
FOR pending IN
|
|
279
|
+
SELECT DISTINCT receipt.* FROM skill_write_receipts receipt
|
|
280
|
+
JOIN skill_source_bindings binding ON binding.preference_id=(receipt.receipt->>'skillId')::uuid
|
|
281
|
+
AND binding.account_id=receipt.account_id AND binding.workspace_id=receipt.workspace_id
|
|
282
|
+
JOIN capability_facet_installations fi ON fi.facet_id=binding.skill_facet_id
|
|
283
|
+
AND fi.account_id=binding.account_id AND fi.workspace_id=binding.workspace_id
|
|
284
|
+
JOIN capability_component_owners owner ON owner.facet_installation_id=fi.id
|
|
285
|
+
AND owner.account_id=fi.account_id AND owner.workspace_id=fi.workspace_id
|
|
286
|
+
WHERE receipt.account_id=NEW.account_id AND receipt.workspace_id=NEW.workspace_id
|
|
287
|
+
AND receipt.receipt ? 'deferredPublication' AND owner.owner_kind=publication_owner_kind AND owner.owner_id=NEW.id::text
|
|
288
|
+
ORDER BY receipt.created_at,receipt.operation_id
|
|
289
|
+
LOOP
|
|
290
|
+
publication_hash := md5('skill-publication:'||pending.operation_id);
|
|
291
|
+
publication_id := (substr(publication_hash,1,12)||'5'||substr(publication_hash,14,3)||'a'||substr(publication_hash,18,15))::uuid;
|
|
292
|
+
expected_fingerprint := encode(sha256(convert_to('skill-publication:'||pending.fingerprint,'UTF8')),'hex');
|
|
293
|
+
IF EXISTS(SELECT 1 FROM skill_write_receipts existing WHERE existing.workspace_id=NEW.workspace_id AND existing.operation_id=publication_id) THEN
|
|
294
|
+
IF NOT EXISTS(SELECT 1 FROM skill_write_receipts existing WHERE existing.workspace_id=NEW.workspace_id AND existing.operation_id=publication_id
|
|
295
|
+
AND existing.fingerprint=expected_fingerprint AND existing.receipt->>'sourceOperationId'=pending.operation_id::text) THEN
|
|
296
|
+
RAISE EXCEPTION 'Skill publication operation identity collision' USING ERRCODE='23505';
|
|
297
|
+
END IF;
|
|
298
|
+
CONTINUE;
|
|
299
|
+
END IF;
|
|
300
|
+
publication_operation := nullif(current_setting('opengeni.skill_publication_operation_id',true),'')::uuid;
|
|
301
|
+
SELECT plugin_key INTO parent_target FROM capability_plugins WHERE id=NEW.plugin_id;
|
|
302
|
+
IF publication_operation IS NULL OR NOT EXISTS(SELECT 1 FROM capability_operations operation
|
|
303
|
+
WHERE operation.id=publication_operation AND operation.account_id=NEW.account_id AND operation.workspace_id=NEW.workspace_id
|
|
304
|
+
AND operation.target_kind=publication_owner_kind AND operation.target_id=parent_target AND operation.status='running') THEN
|
|
305
|
+
RAISE EXCEPTION 'Skill publication requires a claimed parent finalization' USING ERRCODE='42501';
|
|
306
|
+
END IF;
|
|
307
|
+
SELECT * INTO head FROM preference_registry_preferences WHERE id=(pending.receipt->>'skillId')::uuid AND account_id=NEW.account_id FOR UPDATE;
|
|
308
|
+
SELECT * INTO revision FROM preference_registry_revisions WHERE id=(pending.receipt->>'revisionId')::uuid AND account_id=NEW.account_id;
|
|
309
|
+
disposition := 'preserved'; event_id := NULL; current_mode := 'automatic';
|
|
310
|
+
IF pending.actor->>'kind' <> 'human' THEN
|
|
311
|
+
-- Deferred publication uses its original accepted policy. Historical
|
|
312
|
+
-- receipts receive no new automatic authority during migration.
|
|
313
|
+
current_mode:=coalesce(pending.receipt#>>'{learningPolicy,effective,skills}','review_first');
|
|
314
|
+
IF current_mode='review_first' THEN current_mode:='suggest'; END IF;
|
|
315
|
+
END IF;
|
|
316
|
+
IF pending.actor->>'kind'='agent' THEN
|
|
317
|
+
PERFORM 1 FROM sessions session JOIN session_turns turn ON turn.id=session.active_turn_id AND turn.session_id=session.id
|
|
318
|
+
JOIN session_turn_attempts attempt ON attempt.id=turn.active_attempt_id AND attempt.turn_id=turn.id
|
|
319
|
+
WHERE session.id=(pending.actor->>'sessionId')::uuid AND session.account_id=NEW.account_id AND session.workspace_id=NEW.workspace_id
|
|
320
|
+
AND turn.id=(pending.actor->>'turnId')::uuid AND turn.account_id=NEW.account_id AND turn.workspace_id=NEW.workspace_id
|
|
321
|
+
AND turn.status IN ('running','requires_action','recovering','waiting_capacity')
|
|
322
|
+
AND attempt.id=(pending.actor->>'attemptId')::uuid AND attempt.account_id=NEW.account_id AND attempt.workspace_id=NEW.workspace_id
|
|
323
|
+
AND attempt.session_id=session.id AND attempt.execution_generation=(pending.actor->>'executionGeneration')::integer
|
|
324
|
+
AND turn.execution_generation=attempt.execution_generation AND attempt.state IN ('claimed','running')
|
|
325
|
+
AND NOT EXISTS(SELECT 1 FROM session_attempt_interruptions interruption WHERE interruption.workspace_id=NEW.workspace_id
|
|
326
|
+
AND interruption.attempt_id=attempt.id AND interruption.state IN ('pending','delivered','acknowledged'))
|
|
327
|
+
FOR SHARE OF session,turn,attempt;
|
|
328
|
+
IF NOT FOUND THEN current_mode := 'suggest'; END IF;
|
|
329
|
+
END IF;
|
|
330
|
+
IF head.status NOT IN ('rejected','superseded') AND head.scope='workspace' AND head.scope_workspace_id=NEW.workspace_id
|
|
331
|
+
AND head.scope_version=(pending.receipt->'deferredPublication'->>'expectedScopeVersion')::integer
|
|
332
|
+
AND head.active_revision_id IS NOT DISTINCT FROM (pending.receipt->'deferredPublication'->>'expectedRevisionId')::uuid
|
|
333
|
+
AND revision.revision=(SELECT max(r.revision) FROM preference_registry_revisions r WHERE r.preference_id=head.id)
|
|
334
|
+
AND (revision.expires_at IS NULL OR revision.expires_at>clock_timestamp())
|
|
335
|
+
AND EXISTS(SELECT 1 FROM skill_source_bindings binding WHERE binding.preference_id=head.id
|
|
336
|
+
AND binding.skill_facet_id=(pending.receipt->'deferredPublication'->>'sourceFacetId')::uuid)
|
|
337
|
+
AND skill_source_has_effective_owner(NEW.account_id,NEW.workspace_id,(pending.receipt->'deferredPublication'->>'sourceFacetId')::uuid)
|
|
338
|
+
THEN
|
|
339
|
+
IF current_mode='automatic' THEN
|
|
340
|
+
PERFORM set_config('opengeni.preference_lifecycle_head_id',head.id::text,true);
|
|
341
|
+
PERFORM set_config('opengeni.preference_lifecycle_operation','activate',true);
|
|
342
|
+
UPDATE preference_registry_preferences SET status='active',active_revision_id=revision.id,
|
|
343
|
+
active_revision=revision.revision,active_content_hash=revision.content_hash,activation_version=activation_version+1,updated_at=clock_timestamp()
|
|
344
|
+
WHERE id=head.id;
|
|
345
|
+
SELECT coalesce(max(version),0)+1 INTO next_event FROM preference_registry_events WHERE preference_id=head.id;
|
|
346
|
+
INSERT INTO preference_registry_events(account_id,preference_id,type,version,old_revision_id,new_revision_id,actor_subject_id,reason)
|
|
347
|
+
VALUES(NEW.account_id,head.id,'activated',next_event,head.active_revision_id,revision.id,'service:skill-publication',
|
|
348
|
+
'Publish previously authorized Skill after composite owner finalization') RETURNING id INTO event_id;
|
|
349
|
+
disposition := 'applied';
|
|
350
|
+
ELSE disposition := 'pending'; END IF;
|
|
351
|
+
END IF;
|
|
352
|
+
result := jsonb_build_object('operationId',publication_id,'sourceOperationId',pending.operation_id,
|
|
353
|
+
'skillId',pending.receipt->>'skillId','revisionId',pending.receipt->>'revisionId','outcome',disposition,
|
|
354
|
+
'activationEventId',event_id,'replayed',false,'publicationOperationId',publication_operation);
|
|
355
|
+
IF disposition='pending' THEN result := result || jsonb_build_object('pendingReason','approval'); END IF;
|
|
356
|
+
INSERT INTO skill_write_receipts(account_id,workspace_id,operation_id,fingerprint,actor,receipt,activation_event_id)
|
|
357
|
+
VALUES(NEW.account_id,NEW.workspace_id,publication_id,expected_fingerprint,publication_actor,result,event_id);
|
|
358
|
+
END LOOP;
|
|
359
|
+
RETURN NEW;
|
|
360
|
+
END $$;
|
|
361
|
+
|
|
362
|
+
-- CREATE OR REPLACE resets function-local settings. Preserve the original
|
|
363
|
+
-- fixed namespace: planning nested source-owner checks must not inherit the
|
|
364
|
+
-- runtime caller's search path or lose the lifecycle guard's resolution.
|
|
365
|
+
DO $function_paths$
|
|
366
|
+
BEGIN
|
|
367
|
+
EXECUTE format('ALTER FUNCTION skill_source_has_effective_owner(uuid,uuid,uuid) SET search_path = %I, pg_catalog, pg_temp', current_schema());
|
|
368
|
+
EXECUTE format('ALTER FUNCTION skill_publish_finalized_owner() SET search_path = %I, pg_catalog, pg_temp', current_schema());
|
|
369
|
+
END $function_paths$;
|
|
370
|
+
|
|
371
|
+
-- Do not plan the restricted source-owner query for a new, inactive head.
|
|
372
|
+
-- A non-bypass migration owner exposes this distinction during runtime INSERT:
|
|
373
|
+
-- PostgreSQL may initialize an AND subplan even when active_revision_id is NULL.
|
|
374
|
+
-- Active revisions still pass the same source and files-bearing guards.
|
|
375
|
+
CREATE OR REPLACE FUNCTION skill_guard_legacy_activation() RETURNS trigger
|
|
376
|
+
LANGUAGE plpgsql SET search_path FROM CURRENT AS $$
|
|
377
|
+
BEGIN
|
|
378
|
+
IF NEW.active_revision_id IS NOT NULL THEN
|
|
379
|
+
IF (TG_OP='INSERT' OR NEW.active_revision_id IS DISTINCT FROM OLD.active_revision_id)
|
|
380
|
+
AND EXISTS(SELECT 1 FROM preference_registry_revisions target JOIN skill_source_bindings binding
|
|
381
|
+
ON binding.preference_id=target.preference_id AND binding.account_id=target.account_id
|
|
382
|
+
WHERE target.id=NEW.active_revision_id AND target.provenance_source='portable_skill'
|
|
383
|
+
AND NOT skill_source_has_effective_owner(binding.account_id,binding.workspace_id,target.provenance_source_id::uuid)) THEN
|
|
384
|
+
RAISE EXCEPTION 'Skill activation requires a finalized source owner' USING ERRCODE='42501';
|
|
385
|
+
END IF;
|
|
386
|
+
IF EXISTS(SELECT 1 FROM preference_registry_revisions target
|
|
387
|
+
WHERE target.account_id=NEW.account_id AND target.preference_id=NEW.id
|
|
388
|
+
AND target.id=NEW.active_revision_id AND target.skill_files IS NULL) THEN
|
|
389
|
+
RAISE EXCEPTION 'Skill folder activation requires a files-bearing revision; use unified restore' USING ERRCODE='55000';
|
|
390
|
+
END IF;
|
|
391
|
+
END IF;
|
|
392
|
+
RETURN NEW;
|
|
393
|
+
END $$;
|
|
394
|
+
|
|
395
|
+
SET CONSTRAINTS ALL IMMEDIATE;
|
|
396
|
+
DROP TABLE pack_installation_components;
|
|
397
|
+
DROP TABLE pack_installations;
|
|
398
|
+
DROP TABLE workspace_packs;
|
|
399
|
+
DROP FUNCTION pack_v2_validate_installation();
|
|
400
|
+
DROP FUNCTION pack_v2_validate_component();
|
|
401
|
+
|
|
402
|
+
ALTER TABLE capability_operations FORCE ROW LEVEL SECURITY;
|
|
403
|
+
ALTER TABLE capability_component_owners FORCE ROW LEVEL SECURITY;
|
|
404
|
+
ALTER TABLE capability_facet_installations FORCE ROW LEVEL SECURITY;
|
|
405
|
+
ALTER TABLE capability_plugin_installations FORCE ROW LEVEL SECURITY;
|
|
406
|
+
ALTER TABLE integration_facet_binding_owners FORCE ROW LEVEL SECURITY;
|
|
407
|
+
ALTER TABLE integration_facet_bindings FORCE ROW LEVEL SECURITY;
|
|
408
|
+
ALTER TABLE skill_source_bindings FORCE ROW LEVEL SECURITY;
|
|
409
|
+
ALTER TABLE preference_registry_preferences FORCE ROW LEVEL SECURITY;
|
|
410
|
+
ALTER TABLE preference_registry_revisions FORCE ROW LEVEL SECURITY;
|
|
411
|
+
ALTER TABLE preference_registry_events FORCE ROW LEVEL SECURITY;
|
|
412
|
+
ALTER TABLE automation_sources FORCE ROW LEVEL SECURITY;
|
|
413
|
+
ALTER TABLE automation_triggers FORCE ROW LEVEL SECURITY;
|
|
414
|
+
ALTER TABLE automation_runs FORCE ROW LEVEL SECURITY;
|
|
415
|
+
ALTER TABLE automation_trigger_revisions FORCE ROW LEVEL SECURITY;
|
|
416
|
+
ALTER TABLE automation_trigger_events FORCE ROW LEVEL SECURITY;
|
|
417
|
+
ALTER TABLE automation_run_event_links FORCE ROW LEVEL SECURITY;
|