@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,224 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Session attachment grants do not change original-file ownership or history.
|
|
3
|
+
SET LOCAL lock_timeout = '5s';
|
|
4
|
+
SET LOCAL statement_timeout = '5min';
|
|
5
|
+
|
|
6
|
+
CREATE TABLE opengeni_private.session_file_attachments (
|
|
7
|
+
account_id uuid NOT NULL,
|
|
8
|
+
workspace_id uuid NOT NULL,
|
|
9
|
+
session_id uuid NOT NULL,
|
|
10
|
+
file_id uuid NOT NULL,
|
|
11
|
+
accepted_by text NOT NULL,
|
|
12
|
+
accepted_event_id uuid NOT NULL,
|
|
13
|
+
accepted_at timestamptz NOT NULL DEFAULT now(),
|
|
14
|
+
PRIMARY KEY (account_id, workspace_id, session_id, file_id),
|
|
15
|
+
FOREIGN KEY (account_id, workspace_id, file_id) REFERENCES files(account_id, workspace_id, id) ON DELETE CASCADE,
|
|
16
|
+
FOREIGN KEY (workspace_id, session_id) REFERENCES sessions(workspace_id, id) ON DELETE CASCADE
|
|
17
|
+
);
|
|
18
|
+
ALTER TABLE opengeni_private.session_file_attachments ENABLE ROW LEVEL SECURITY;
|
|
19
|
+
ALTER TABLE opengeni_private.session_file_attachments FORCE ROW LEVEL SECURITY;
|
|
20
|
+
CREATE POLICY workspace_isolation ON opengeni_private.session_file_attachments
|
|
21
|
+
USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
|
|
22
|
+
WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
|
|
23
|
+
REVOKE ALL ON opengeni_private.session_file_attachments FROM PUBLIC;
|
|
24
|
+
|
|
25
|
+
CREATE TABLE opengeni_private.session_file_read_capabilities (
|
|
26
|
+
backend_pid integer NOT NULL, transaction_id xid8 NOT NULL,
|
|
27
|
+
nonce uuid NOT NULL, account_id uuid NOT NULL, workspace_id uuid NOT NULL, file_id uuid NOT NULL,
|
|
28
|
+
PRIMARY KEY (backend_pid, transaction_id, nonce, file_id)
|
|
29
|
+
);
|
|
30
|
+
REVOKE ALL ON opengeni_private.session_file_read_capabilities FROM PUBLIC;
|
|
31
|
+
CREATE FUNCTION opengeni_private.session_file_read_allowed(p_account uuid, p_workspace uuid, p_file uuid)
|
|
32
|
+
RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS $$
|
|
33
|
+
SELECT EXISTS (SELECT 1 FROM opengeni_private.session_file_read_capabilities c
|
|
34
|
+
WHERE c.backend_pid=pg_backend_pid() AND c.transaction_id=pg_current_xact_id_if_assigned()
|
|
35
|
+
AND c.account_id=p_account AND c.workspace_id=p_workspace AND c.file_id=p_file)
|
|
36
|
+
$$;
|
|
37
|
+
REVOKE ALL ON FUNCTION opengeni_private.session_file_read_allowed(uuid,uuid,uuid) FROM PUBLIC;
|
|
38
|
+
-- Opening a read never opens UPDATE/DELETE or generic personal-file access.
|
|
39
|
+
DROP POLICY files_personal_owner ON files;
|
|
40
|
+
CREATE POLICY files_personal_owner ON files AS RESTRICTIVE FOR SELECT USING (
|
|
41
|
+
private_owner_subject_ids IS NULL
|
|
42
|
+
OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(private_owner_subject_ids)
|
|
43
|
+
OR opengeni_private.session_file_read_allowed(account_id,workspace_id,id)
|
|
44
|
+
);
|
|
45
|
+
CREATE POLICY files_personal_owner_insert ON files AS RESTRICTIVE FOR INSERT WITH CHECK (
|
|
46
|
+
private_owner_subject_ids IS NULL OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(private_owner_subject_ids));
|
|
47
|
+
CREATE POLICY files_personal_owner_update ON files AS RESTRICTIVE FOR UPDATE USING (
|
|
48
|
+
private_owner_subject_ids IS NULL OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(private_owner_subject_ids)) WITH CHECK (
|
|
49
|
+
private_owner_subject_ids IS NULL OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(private_owner_subject_ids));
|
|
50
|
+
CREATE POLICY files_personal_owner_delete ON files AS RESTRICTIVE FOR DELETE USING (
|
|
51
|
+
private_owner_subject_ids IS NULL OR nullif(current_setting('opengeni.private_file_owner',true),'')=ANY(private_owner_subject_ids));
|
|
52
|
+
|
|
53
|
+
CREATE FUNCTION opengeni_private.accept_session_file_attachments(p_account uuid,p_workspace uuid,p_session uuid,p_turn uuid,p_subject text,p_files uuid[])
|
|
54
|
+
RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path=pg_catalog,pg_temp AS $$
|
|
55
|
+
DECLARE accepted_turn record;
|
|
56
|
+
BEGIN
|
|
57
|
+
IF p_account IS DISTINCT FROM opengeni_private.current_account_id()
|
|
58
|
+
OR p_workspace IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
59
|
+
OR p_subject IS NULL OR p_subject IS DISTINCT FROM opengeni_private.current_subject_id()
|
|
60
|
+
OR p_subject IS DISTINCT FROM nullif(current_setting('opengeni.private_file_owner',true),'')
|
|
61
|
+
OR cardinality(p_files)>1000
|
|
62
|
+
THEN RAISE EXCEPTION 'attachment acceptance scope mismatch' USING ERRCODE='42501'; END IF;
|
|
63
|
+
PERFORM 1 FROM sessions WHERE account_id=p_account AND workspace_id=p_workspace AND id=p_session
|
|
64
|
+
AND (visibility='workspace_shared' OR session_private_actor_visible(account_id,workspace_id,owner_organization_membership_id,owner_subject_id)) FOR KEY SHARE;
|
|
65
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'attachment session unavailable' USING ERRCODE='42501'; END IF;
|
|
66
|
+
SELECT t.* INTO accepted_turn FROM session_turns t WHERE t.account_id=p_account AND t.workspace_id=p_workspace
|
|
67
|
+
AND t.session_id=p_session AND t.id=p_turn AND t.source IN ('user','api')
|
|
68
|
+
AND t.initiator_kind='subject' AND t.initiator_subject_id=p_subject;
|
|
69
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'attachment requires accepted human input' USING ERRCODE='42501'; END IF;
|
|
70
|
+
-- Only a completed original upload owned by this verified human is shareable.
|
|
71
|
+
-- Generic personal resources, provider originals and arbitrary history refs do
|
|
72
|
+
-- not become a session grant merely because their UUID appeared in a request.
|
|
73
|
+
INSERT INTO opengeni_private.session_file_attachments(account_id,workspace_id,session_id,file_id,accepted_by,accepted_event_id)
|
|
74
|
+
SELECT p_account,p_workspace,p_session,f.id,p_subject,accepted_turn.trigger_event_id FROM files f
|
|
75
|
+
WHERE f.account_id=p_account AND f.workspace_id=p_workspace AND f.id=ANY(p_files) AND f.status='ready'
|
|
76
|
+
AND p_subject=ANY(f.private_owner_subject_ids)
|
|
77
|
+
AND EXISTS (SELECT 1 FROM session_events e CROSS JOIN LATERAL jsonb_array_elements(
|
|
78
|
+
CASE WHEN jsonb_typeof(e.payload->'resources')='array' THEN e.payload->'resources' ELSE '[]'::jsonb END) r
|
|
79
|
+
WHERE e.account_id=p_account AND e.workspace_id=p_workspace AND e.session_id=p_session
|
|
80
|
+
AND r->>'kind'='file' AND r->>'fileId'=f.id::text
|
|
81
|
+
AND ((e.id=accepted_turn.trigger_event_id AND e.type='user.message') OR (
|
|
82
|
+
e.type='session.created' AND e.payload#>>'{createdBy,kind}'='subject'
|
|
83
|
+
AND e.payload#>>'{createdBy,subjectId}'=p_subject
|
|
84
|
+
AND NOT EXISTS (SELECT 1 FROM session_events prior, session_events current_input
|
|
85
|
+
WHERE prior.account_id=p_account AND prior.workspace_id=p_workspace AND prior.session_id=p_session
|
|
86
|
+
AND current_input.id=accepted_turn.trigger_event_id AND current_input.session_id=p_session
|
|
87
|
+
AND prior.type='user.message' AND prior.sequence<current_input.sequence))))
|
|
88
|
+
AND EXISTS (SELECT 1 FROM file_uploads u WHERE u.account_id=p_account AND u.workspace_id=p_workspace
|
|
89
|
+
AND u.file_id=f.id AND u.status='completed' AND u.private_file_owner_subject_id=p_subject)
|
|
90
|
+
AND google_drive_file_authorized(p_account,p_workspace,p_subject,f.id)
|
|
91
|
+
ON CONFLICT DO NOTHING;
|
|
92
|
+
END $$;
|
|
93
|
+
REVOKE ALL ON FUNCTION opengeni_private.accept_session_file_attachments(uuid,uuid,uuid,uuid,text,uuid[]) FROM PUBLIC;
|
|
94
|
+
|
|
95
|
+
CREATE FUNCTION opengeni_private.read_session_file_attachments(p_account uuid,p_workspace uuid,p_session uuid,p_epoch integer,p_files uuid[],p_actor jsonb)
|
|
96
|
+
RETURNS SETOF files LANGUAGE plpgsql SECURITY DEFINER SET search_path=pg_catalog,pg_temp AS $$
|
|
97
|
+
DECLARE read_nonce uuid:=gen_random_uuid(); session_row record; caller_row record;
|
|
98
|
+
BEGIN
|
|
99
|
+
IF p_account IS DISTINCT FROM opengeni_private.current_account_id()
|
|
100
|
+
OR p_workspace IS DISTINCT FROM opengeni_private.current_workspace_id()
|
|
101
|
+
OR opengeni_private.current_subject_id() IS NULL OR cardinality(p_files)>1000
|
|
102
|
+
THEN RAISE EXCEPTION 'attachment read scope mismatch' USING ERRCODE='42501'; END IF;
|
|
103
|
+
-- Match lifecycle lock order: workspace, ordered sessions, turn, attempt.
|
|
104
|
+
PERFORM 1 FROM workspaces WHERE account_id=p_account AND id=p_workspace FOR KEY SHARE;
|
|
105
|
+
PERFORM 1 FROM sessions WHERE account_id=p_account AND workspace_id=p_workspace
|
|
106
|
+
AND (id=p_session OR (p_actor->>'kind'='agent_attempt' AND id=(p_actor->>'callerSessionId')::uuid))
|
|
107
|
+
ORDER BY id FOR SHARE;
|
|
108
|
+
SELECT * INTO session_row FROM sessions WHERE account_id=p_account AND workspace_id=p_workspace AND id=p_session;
|
|
109
|
+
IF NOT FOUND OR session_row.authority_epoch IS DISTINCT FROM p_epoch
|
|
110
|
+
OR NOT (session_row.visibility='workspace_shared' OR session_private_actor_visible(
|
|
111
|
+
p_account,p_workspace,session_row.owner_organization_membership_id,session_row.owner_subject_id))
|
|
112
|
+
THEN RAISE EXCEPTION 'attachment session changed' USING ERRCODE='42501'; END IF;
|
|
113
|
+
-- Core proves full host/Slack/tree authorization before this read. The DB
|
|
114
|
+
-- additionally rejects a stale exact attempt, including while the host runs.
|
|
115
|
+
IF p_actor->>'kind'='agent_attempt' THEN
|
|
116
|
+
IF p_actor->>'subjectId' IS DISTINCT FROM opengeni_private.current_subject_id() THEN
|
|
117
|
+
RAISE EXCEPTION 'attachment caller identity mismatch' USING ERRCODE='42501';
|
|
118
|
+
END IF;
|
|
119
|
+
SELECT * INTO caller_row FROM sessions WHERE account_id=p_account AND workspace_id=p_workspace
|
|
120
|
+
AND id=(p_actor->>'callerSessionId')::uuid;
|
|
121
|
+
IF NOT FOUND OR caller_row.active_turn_id IS DISTINCT FROM (p_actor->>'turnId')::uuid OR caller_row.status='cancelled' THEN
|
|
122
|
+
RAISE EXCEPTION 'attachment caller stale' USING ERRCODE='42501';
|
|
123
|
+
END IF;
|
|
124
|
+
PERFORM 1 FROM session_turns t WHERE t.account_id=p_account AND t.workspace_id=p_workspace
|
|
125
|
+
AND t.session_id=caller_row.id AND t.id=(p_actor->>'turnId')::uuid
|
|
126
|
+
AND t.status='running' AND t.active_attempt_id=(p_actor->>'attemptId')::uuid
|
|
127
|
+
AND t.execution_generation=(p_actor->>'executionGeneration')::integer FOR SHARE;
|
|
128
|
+
IF NOT FOUND THEN RAISE EXCEPTION 'attachment caller stale' USING ERRCODE='42501'; END IF;
|
|
129
|
+
PERFORM 1 FROM session_turn_attempts a WHERE a.account_id=p_account AND a.workspace_id=p_workspace
|
|
130
|
+
AND a.session_id=caller_row.id AND a.turn_id=(p_actor->>'turnId')::uuid
|
|
131
|
+
AND a.id=(p_actor->>'attemptId')::uuid AND a.state IN ('claimed','running')
|
|
132
|
+
AND a.execution_generation=(p_actor->>'executionGeneration')::integer
|
|
133
|
+
AND a.closed_at IS NULL AND a.quiesced_at IS NULL
|
|
134
|
+
AND a.authority_epoch=caller_row.authority_epoch AND a.authority_visibility=caller_row.visibility
|
|
135
|
+
AND a.authority_owner_organization_membership_id IS NOT DISTINCT FROM caller_row.owner_organization_membership_id FOR SHARE;
|
|
136
|
+
IF NOT FOUND OR EXISTS (SELECT 1 FROM session_attempt_interruptions i
|
|
137
|
+
WHERE i.account_id=p_account AND i.workspace_id=p_workspace AND i.session_id=caller_row.id
|
|
138
|
+
AND i.attempt_id=(p_actor->>'attemptId')::uuid AND i.state IN ('pending','delivered','acknowledged'))
|
|
139
|
+
THEN RAISE EXCEPTION 'attachment caller stale' USING ERRCODE='42501'; END IF;
|
|
140
|
+
ELSIF p_actor->>'kind' IS DISTINCT FROM 'subject' OR p_actor->>'subjectId' IS DISTINCT FROM opengeni_private.current_subject_id() THEN
|
|
141
|
+
RAISE EXCEPTION 'attachment actor unavailable' USING ERRCODE='42501';
|
|
142
|
+
END IF;
|
|
143
|
+
INSERT INTO opengeni_private.session_file_read_capabilities
|
|
144
|
+
SELECT pg_backend_pid(),pg_current_xact_id(),read_nonce,p_account,p_workspace,g.file_id
|
|
145
|
+
FROM opengeni_private.session_file_attachments g
|
|
146
|
+
WHERE g.account_id=p_account AND g.workspace_id=p_workspace AND g.session_id=p_session AND g.file_id=ANY(p_files);
|
|
147
|
+
RETURN QUERY SELECT f.* FROM files f JOIN opengeni_private.session_file_read_capabilities c ON c.file_id=f.id
|
|
148
|
+
WHERE c.backend_pid=pg_backend_pid() AND c.transaction_id=pg_current_xact_id() AND c.nonce=read_nonce
|
|
149
|
+
AND f.account_id=p_account AND f.workspace_id=p_workspace AND f.status='ready'
|
|
150
|
+
AND google_drive_file_authorized(p_account,p_workspace,opengeni_private.current_subject_id(),f.id);
|
|
151
|
+
DELETE FROM opengeni_private.session_file_read_capabilities WHERE nonce=read_nonce AND backend_pid=pg_backend_pid();
|
|
152
|
+
END $$;
|
|
153
|
+
REVOKE ALL ON FUNCTION opengeni_private.read_session_file_attachments(uuid,uuid,uuid,integer,uuid[],jsonb) FROM PUBLIC;
|
|
154
|
+
|
|
155
|
+
DO $grants$
|
|
156
|
+
DECLARE data_schema text:=current_schema(); recipient record;
|
|
157
|
+
BEGIN
|
|
158
|
+
EXECUTE format('ALTER FUNCTION opengeni_private.accept_session_file_attachments(uuid,uuid,uuid,uuid,text,uuid[]) SET search_path=pg_catalog,%I,pg_temp',data_schema);
|
|
159
|
+
EXECUTE format('ALTER FUNCTION opengeni_private.read_session_file_attachments(uuid,uuid,uuid,integer,uuid[],jsonb) SET search_path=pg_catalog,%I,pg_temp',data_schema);
|
|
160
|
+
FOR recipient IN SELECT DISTINCT r.rolname FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace
|
|
161
|
+
CROSS JOIN LATERAL aclexplode(c.relacl) acl JOIN pg_roles r ON r.oid=acl.grantee
|
|
162
|
+
WHERE n.nspname=data_schema AND c.relname='files' AND acl.privilege_type='SELECT' AND acl.grantee<>c.relowner
|
|
163
|
+
LOOP
|
|
164
|
+
EXECUTE format('REVOKE ALL ON TABLE opengeni_private.session_file_attachments,opengeni_private.session_file_read_capabilities FROM %I',recipient.rolname);
|
|
165
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION opengeni_private.session_file_read_allowed(uuid,uuid,uuid),opengeni_private.accept_session_file_attachments(uuid,uuid,uuid,uuid,text,uuid[]),opengeni_private.read_session_file_attachments(uuid,uuid,uuid,integer,uuid[],jsonb) TO %I',recipient.rolname);
|
|
166
|
+
END LOOP;
|
|
167
|
+
END $grants$;
|
|
168
|
+
|
|
169
|
+
-- Existing explicit human attachments receive the same grant. Ownership and
|
|
170
|
+
-- event history stay untouched. NO FORCE is transaction-local in effect: DDL
|
|
171
|
+
-- locks prevent other transactions observing this migration's intermediate state.
|
|
172
|
+
ALTER TABLE files NO FORCE ROW LEVEL SECURITY;
|
|
173
|
+
ALTER TABLE file_uploads NO FORCE ROW LEVEL SECURITY;
|
|
174
|
+
ALTER TABLE session_events NO FORCE ROW LEVEL SECURITY;
|
|
175
|
+
ALTER TABLE session_turns NO FORCE ROW LEVEL SECURITY;
|
|
176
|
+
ALTER TABLE opengeni_private.session_file_attachments NO FORCE ROW LEVEL SECURITY;
|
|
177
|
+
INSERT INTO opengeni_private.session_file_attachments
|
|
178
|
+
(account_id,workspace_id,session_id,file_id,accepted_by,accepted_event_id,accepted_at)
|
|
179
|
+
SELECT DISTINCT ON (t.account_id,t.workspace_id,t.session_id,f.id)
|
|
180
|
+
t.account_id,t.workspace_id,t.session_id,f.id,t.initiator_subject_id,e.id,e.occurred_at
|
|
181
|
+
FROM session_turns t JOIN session_events e ON e.id=t.trigger_event_id
|
|
182
|
+
AND e.account_id=t.account_id AND e.workspace_id=t.workspace_id AND e.session_id=t.session_id
|
|
183
|
+
CROSS JOIN LATERAL jsonb_array_elements(CASE WHEN jsonb_typeof(e.payload->'resources')='array'
|
|
184
|
+
THEN e.payload->'resources' ELSE '[]'::jsonb END) r
|
|
185
|
+
JOIN files f ON f.account_id=t.account_id AND f.workspace_id=t.workspace_id
|
|
186
|
+
AND f.id::text=r->>'fileId' AND r->>'kind'='file'
|
|
187
|
+
WHERE t.source IN ('user','api') AND t.initiator_kind='subject' AND e.type='user.message'
|
|
188
|
+
AND f.status='ready' AND t.initiator_subject_id=ANY(f.private_owner_subject_ids)
|
|
189
|
+
AND EXISTS (SELECT 1 FROM file_uploads u WHERE u.account_id=f.account_id AND u.workspace_id=f.workspace_id
|
|
190
|
+
AND u.file_id=f.id AND u.status='completed' AND u.private_file_owner_subject_id=t.initiator_subject_id)
|
|
191
|
+
ORDER BY t.account_id,t.workspace_id,t.session_id,f.id,e.sequence
|
|
192
|
+
ON CONFLICT DO NOTHING;
|
|
193
|
+
-- Copy only already accepted attachment grants at the existing authorized fork
|
|
194
|
+
-- boundary. A message fork cannot acquire files attached after its cut point.
|
|
195
|
+
DO $forks$
|
|
196
|
+
DECLARE candidate record; definition text; replacement text; marker text;
|
|
197
|
+
BEGIN
|
|
198
|
+
marker := ') RETURNING id INTO event_row_id;';
|
|
199
|
+
FOR candidate IN SELECT oid,pronargs FROM pg_proc WHERE proname='fork_session_content'
|
|
200
|
+
AND pronamespace=current_schema()::regnamespace AND pronargs IN (10,11)
|
|
201
|
+
LOOP
|
|
202
|
+
definition := pg_get_functiondef(candidate.oid);
|
|
203
|
+
replacement := marker || E'\n\n' || $copy$
|
|
204
|
+
INSERT INTO opengeni_private.session_file_attachments
|
|
205
|
+
(account_id,workspace_id,session_id,file_id,accepted_by,accepted_event_id,accepted_at)
|
|
206
|
+
SELECT g.account_id,g.workspace_id,destination_session_id,g.file_id,g.accepted_by,event_row_id,clock_timestamp()
|
|
207
|
+
FROM opengeni_private.session_file_attachments g
|
|
208
|
+
WHERE g.account_id=p_account_id AND g.workspace_id=p_source_workspace_id AND g.session_id=p_source_session_id
|
|
209
|
+
$copy$ || CASE WHEN candidate.pronargs=11 THEN $boundary$
|
|
210
|
+
AND EXISTS (SELECT 1 FROM session_events e WHERE e.id=g.accepted_event_id
|
|
211
|
+
AND e.account_id=p_account_id AND e.workspace_id=p_source_workspace_id
|
|
212
|
+
AND e.session_id=p_source_session_id AND e.sequence<=selected_event.sequence)
|
|
213
|
+
$boundary$ ELSE '' END || E' ON CONFLICT DO NOTHING;';
|
|
214
|
+
IF strpos(definition,marker)=0 THEN RAISE EXCEPTION 'attachment fork seam missing'; END IF;
|
|
215
|
+
EXECUTE replace(definition,marker,replacement);
|
|
216
|
+
END LOOP;
|
|
217
|
+
END $forks$;
|
|
218
|
+
|
|
219
|
+
-- Restore owner isolation after installing the scoped fork definitions.
|
|
220
|
+
ALTER TABLE files FORCE ROW LEVEL SECURITY;
|
|
221
|
+
ALTER TABLE file_uploads FORCE ROW LEVEL SECURITY;
|
|
222
|
+
ALTER TABLE session_events FORCE ROW LEVEL SECURITY;
|
|
223
|
+
ALTER TABLE session_turns FORCE ROW LEVEL SECURITY;
|
|
224
|
+
ALTER TABLE opengeni_private.session_file_attachments FORCE ROW LEVEL SECURITY;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Expand the execution fence independently of the later sharing cutover.
|
|
3
|
+
-- Old and new readers both retain strict revocation until 0501 is applied.
|
|
4
|
+
SET LOCAL lock_timeout = '5s';
|
|
5
|
+
SET LOCAL statement_timeout = '5min';
|
|
6
|
+
ALTER TABLE sessions ADD COLUMN execution_authority_epoch integer NOT NULL DEFAULT 1;
|
|
7
|
+
ALTER TABLE sessions NO FORCE ROW LEVEL SECURITY;
|
|
8
|
+
DO $backfill$
|
|
9
|
+
DECLARE workspace_id_value uuid;
|
|
10
|
+
BEGIN
|
|
11
|
+
FOR workspace_id_value IN SELECT DISTINCT workspace_id FROM sessions ORDER BY workspace_id LOOP
|
|
12
|
+
PERFORM acquire_session_tenancy_fence(workspace_id_value);
|
|
13
|
+
UPDATE sessions SET execution_authority_epoch = authority_epoch
|
|
14
|
+
WHERE workspace_id = workspace_id_value;
|
|
15
|
+
END LOOP;
|
|
16
|
+
END
|
|
17
|
+
$backfill$;
|
|
18
|
+
ALTER TABLE sessions FORCE ROW LEVEL SECURITY;
|
|
19
|
+
ALTER TABLE sessions ADD CONSTRAINT sessions_execution_authority_epoch_bounds
|
|
20
|
+
CHECK (execution_authority_epoch > 0 AND execution_authority_epoch <= authority_epoch);
|
|
21
|
+
CREATE FUNCTION derive_session_execution_authority_epoch() RETURNS trigger
|
|
22
|
+
LANGUAGE plpgsql AS $$
|
|
23
|
+
BEGIN
|
|
24
|
+
IF TG_OP = 'INSERT' THEN
|
|
25
|
+
NEW.execution_authority_epoch := NEW.authority_epoch;
|
|
26
|
+
ELSIF NEW.execution_authority_epoch IS DISTINCT FROM OLD.execution_authority_epoch THEN
|
|
27
|
+
RAISE EXCEPTION 'execution authority floor is lifecycle-owned' USING ERRCODE='42501';
|
|
28
|
+
ELSIF NEW.authority_epoch IS DISTINCT FROM OLD.authority_epoch THEN
|
|
29
|
+
NEW.execution_authority_epoch := NEW.authority_epoch;
|
|
30
|
+
END IF;
|
|
31
|
+
RETURN NEW;
|
|
32
|
+
END $$;
|
|
33
|
+
REVOKE ALL ON FUNCTION derive_session_execution_authority_epoch() FROM PUBLIC;
|
|
34
|
+
-- Runs after the existing authority capability guard. Neither function permits
|
|
35
|
+
-- callers to nominate an execution floor or bypass a visibility/owner fence.
|
|
36
|
+
CREATE TRIGGER sessions_z_execution_epoch BEFORE INSERT OR UPDATE ON sessions
|
|
37
|
+
FOR EACH ROW EXECUTE FUNCTION derive_session_execution_authority_epoch();
|