@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.
Files changed (212) hide show
  1. package/dist/automations.d.ts +0 -2
  2. package/dist/canonical-human-identities.d.ts +4 -0
  3. package/dist/canonical-human-identities.js +3 -3
  4. package/dist/capability-components.d.ts +5 -13
  5. package/dist/capability-integrations.d.ts +23 -8
  6. package/dist/capability-reconciliation.d.ts +5 -0
  7. package/dist/capability-reconciliation.js +21 -0
  8. package/dist/capability-reconciliation.js.map +1 -0
  9. package/dist/{chunk-6T2GGYWE.js → chunk-4UJCXBR2.js} +4 -5800
  10. package/dist/chunk-4UJCXBR2.js.map +1 -0
  11. package/dist/{chunk-MWMXWHJD.js → chunk-7JCDSMX2.js} +2 -2
  12. package/dist/chunk-AJA2ZIUW.js +219 -0
  13. package/dist/chunk-AJA2ZIUW.js.map +1 -0
  14. package/dist/chunk-CDAT6EKW.js +247 -0
  15. package/dist/chunk-CDAT6EKW.js.map +1 -0
  16. package/dist/{chunk-DPONPYJD.js → chunk-CJI6G254.js} +70 -18
  17. package/dist/chunk-CJI6G254.js.map +1 -0
  18. package/dist/chunk-G32NVMRJ.js +276 -0
  19. package/dist/chunk-G32NVMRJ.js.map +1 -0
  20. package/dist/chunk-GABXCG3N.js +5811 -0
  21. package/dist/chunk-GABXCG3N.js.map +1 -0
  22. package/dist/{chunk-ERKVSPDX.js → chunk-LVY33EWL.js} +3 -3
  23. package/dist/{chunk-45CP2YKY.js → chunk-MAMBI2HR.js} +9 -3
  24. package/dist/{chunk-45CP2YKY.js.map → chunk-MAMBI2HR.js.map} +1 -1
  25. package/dist/{chunk-F73UKJY6.js → chunk-MN5PZMEK.js} +91 -108
  26. package/dist/chunk-MN5PZMEK.js.map +1 -0
  27. package/dist/chunk-PJI4MUZ7.js +131 -0
  28. package/dist/chunk-PJI4MUZ7.js.map +1 -0
  29. package/dist/{chunk-T6BWIGWJ.js → chunk-RIYHP2WU.js} +2 -2
  30. package/dist/{chunk-7HMK3B45.js → chunk-RL2MUXM3.js} +16 -7
  31. package/dist/chunk-RL2MUXM3.js.map +1 -0
  32. package/dist/{chunk-HQDVI2DB.js → chunk-SYKHLPTK.js} +4 -3
  33. package/dist/{chunk-HQDVI2DB.js.map → chunk-SYKHLPTK.js.map} +1 -1
  34. package/dist/{chunk-T6YLHJLS.js → chunk-XIXVVBW2.js} +198 -51
  35. package/dist/chunk-XIXVVBW2.js.map +1 -0
  36. package/dist/chunk-ZPJ6V2VG.js +156 -0
  37. package/dist/chunk-ZPJ6V2VG.js.map +1 -0
  38. package/dist/codex-capacity-recovery.d.ts +12 -0
  39. package/dist/codex-failure-eligibility.d.ts +9 -0
  40. package/dist/company-profile-schema.d.ts +4 -2
  41. package/dist/connect-attempts.d.ts +15 -0
  42. package/dist/connect-attempts.js +25 -0
  43. package/dist/connect-attempts.js.map +1 -0
  44. package/dist/connection-authority.d.ts +7 -63
  45. package/dist/connection-refresh-lock.d.ts +4 -0
  46. package/dist/connection-token-resolver.d.ts +8 -43
  47. package/dist/database-timing.d.ts +10 -0
  48. package/dist/database.d.ts +4 -0
  49. package/dist/editable-artifact-durable-export.js +2 -2
  50. package/dist/editable-artifacts.d.ts +9 -0
  51. package/dist/editable-artifacts.js +7 -5
  52. package/dist/index.d.ts +447 -78
  53. package/dist/index.js +12705 -12872
  54. package/dist/index.js.map +1 -1
  55. package/dist/insights-usage-bundle.d.ts +3 -0
  56. package/dist/integration-bindings.d.ts +1 -1
  57. package/dist/integration-facets.d.ts +18 -1
  58. package/dist/knowledge-entries-schema.d.ts +2 -0
  59. package/dist/knowledge-entries.d.ts +1 -0
  60. package/dist/knowledge-indexing.d.ts +1 -0
  61. package/dist/live-session-attempt.d.ts +35 -137
  62. package/dist/managed-auth-session-sets.js +3 -3
  63. package/dist/mcp-account-bindings.d.ts +4 -0
  64. package/dist/mcp-oauth.d.ts +11 -0
  65. package/dist/mcp-operations.js +2 -2
  66. package/dist/migrate.js +1 -1
  67. package/dist/organization-integration-policy.d.ts +51 -0
  68. package/dist/organization-integration-policy.js +18 -0
  69. package/dist/organization-integration-policy.js.map +1 -0
  70. package/dist/organization-usage.d.ts +20 -0
  71. package/dist/plugin-packages.d.ts +9 -12
  72. package/dist/pr-review.d.ts +0 -11
  73. package/dist/preference-registry-schema.d.ts +17 -0
  74. package/dist/provision-roles.js +1 -1
  75. package/dist/retained-provider-commands.d.ts +124 -6
  76. package/dist/retained-provider-commands.js +29 -97
  77. package/dist/retained-provider-commands.js.map +1 -1
  78. package/dist/retired-session-create-metadata.d.ts +4 -0
  79. package/dist/runtime-posture.d.ts +7 -5
  80. package/dist/schema.d.ts +876 -656
  81. package/dist/schema.js +5 -7
  82. package/dist/session-admission-block.d.ts +32 -0
  83. package/dist/session-attempt-writers.d.ts +4 -0
  84. package/dist/session-background-commands.js +5 -4
  85. package/dist/session-command-output.js +8 -126
  86. package/dist/session-command-output.js.map +1 -1
  87. package/dist/session-control.d.ts +5 -1
  88. package/dist/session-event-slices.d.ts +10 -0
  89. package/dist/session-event-slices.js +8 -222
  90. package/dist/session-event-slices.js.map +1 -1
  91. package/dist/session-file-attachments.d.ts +28 -0
  92. package/dist/session-goal-reports.d.ts +43 -0
  93. package/dist/session-mcp-approval.d.ts +4 -0
  94. package/dist/session-mcp-credential-rotation.js +5 -4
  95. package/dist/session-mcp-credential-rotation.js.map +1 -1
  96. package/dist/session-message-search.d.ts +22 -0
  97. package/dist/session-queue-commands.d.ts +2 -6
  98. package/dist/session-retry.d.ts +36 -0
  99. package/dist/session-tenancy.js +3 -3
  100. package/dist/skill-source-release-impact.d.ts +18 -0
  101. package/dist/skill-source-release.d.ts +9 -0
  102. package/dist/skills.d.ts +7 -1
  103. package/dist/user-resource-authority.d.ts +0 -8
  104. package/dist/video-generation.js +3 -3
  105. package/dist/workspace-tool-defaults.js +5 -4
  106. package/dist/workspace-tool-defaults.js.map +1 -1
  107. package/drizzle/0468_knowledge_relationship_projection.sql +30 -0
  108. package/drizzle/0469_knowledge_source_discovery.sql +85 -0
  109. package/drizzle/0470_organization_integration_policy.sql +151 -0
  110. package/drizzle/0471_usage_events_account_recent_index.sql +4 -0
  111. package/drizzle/0472_usage_events_workspace_recent_index.sql +4 -0
  112. package/drizzle/0473_organization_usage_analytical_capability.sql +237 -0
  113. package/drizzle/0474_goal_report_requirements.sql +156 -0
  114. package/drizzle/0475_session_event_history_statistics.sql +15 -0
  115. package/drizzle/0476_session_event_history_policy_planning.sql +29 -0
  116. package/drizzle/0477_managed_sign_in_methods.sql +303 -0
  117. package/drizzle/0478_sender_owned_connections.sql +2772 -0
  118. package/drizzle/0479_connection_create_idempotency.sql +16 -0
  119. package/drizzle/0480_private_session_organization_key_administration.sql +154 -0
  120. package/drizzle/0481_personal_connection_visibility_cleanup.sql +45 -0
  121. package/drizzle/0482_remove_packs.sql +417 -0
  122. package/drizzle/0483_preclaim_admission_block.sql +85 -0
  123. package/drizzle/0484_insights_usage_projection.sql +211 -0
  124. package/drizzle/0485_inherited_mcp_approval_policies.sql +6 -0
  125. package/drizzle/0486_reasoning_configuration_message_forks.sql +35 -0
  126. package/drizzle/0487_skill_catalog_message_forks.sql +26 -0
  127. package/drizzle/0488_permanent_skill_removal.sql +207 -0
  128. package/drizzle/0489_modal_command_byte_offsets.sql +17 -0
  129. package/drizzle/0490_periodic_capture_attempt_cadence.sql +6 -0
  130. package/drizzle/0491_warm_capture_holder_reclamation.sql +57 -0
  131. package/drizzle/0492_codex_accepted_source_authority.sql +249 -0
  132. package/drizzle/0493_stopping_command_error_containment.sql +58 -0
  133. package/drizzle/0494_mcp_account_bindings.sql +334 -0
  134. package/drizzle/0495_consented_sandbox_recovery.sql +220 -0
  135. package/drizzle/0496_supervised_command_settlement.sql +386 -0
  136. package/drizzle/0497_modal_inventory_owner_read_capability.sql +112 -0
  137. package/drizzle/0498_enrollment_membership_fence.sql +87 -0
  138. package/drizzle/0499_session_attachment_access.sql +224 -0
  139. package/drizzle/0500_session_execution_epoch.sql +37 -0
  140. package/drizzle/0501_session_sharing_execution.sql +591 -0
  141. package/drizzle/0502_active_message_boundary_forks.sql +30 -0
  142. package/package.json +17 -5
  143. package/src/automations.ts +17 -73
  144. package/src/canonical-human-identities.ts +17 -5
  145. package/src/capability-components.ts +7 -24
  146. package/src/capability-integrations.ts +301 -21
  147. package/src/capability-reconciliation.ts +19 -0
  148. package/src/codex-capacity-recovery.ts +47 -0
  149. package/src/codex-failure-eligibility.ts +93 -0
  150. package/src/connect-attempts.ts +49 -18
  151. package/src/connection-authority.ts +12 -158
  152. package/src/connection-refresh-lock.ts +22 -0
  153. package/src/connection-token-resolver.ts +58 -530
  154. package/src/database-timing.ts +43 -0
  155. package/src/database.ts +61 -26
  156. package/src/editable-artifacts.ts +1 -1
  157. package/src/index.ts +3578 -1273
  158. package/src/insights-model-bundle.ts +19 -7
  159. package/src/insights-usage-bundle.ts +16 -19
  160. package/src/integration-bindings.ts +2 -7
  161. package/src/integration-facets.ts +73 -6
  162. package/src/knowledge-entries.ts +8 -1
  163. package/src/live-session-attempt.ts +0 -22
  164. package/src/mcp-account-bindings.ts +12 -0
  165. package/src/mcp-oauth.ts +26 -0
  166. package/src/organization-integration-policy.ts +235 -0
  167. package/src/organization-usage.ts +83 -0
  168. package/src/plugin-packages.ts +226 -42
  169. package/src/pr-review.ts +7 -69
  170. package/src/preference-registry-schema.ts +1 -0
  171. package/src/provision-roles.ts +50 -37
  172. package/src/retained-provider-commands.ts +286 -9
  173. package/src/retired-session-create-metadata.ts +16 -0
  174. package/src/runtime-posture.ts +217 -13
  175. package/src/schema.ts +104 -120
  176. package/src/session-admission-block.ts +29 -0
  177. package/src/session-attempt-writers.ts +48 -0
  178. package/src/session-control.ts +21 -5
  179. package/src/session-event-slices.ts +76 -26
  180. package/src/session-file-attachments.ts +66 -0
  181. package/src/session-goal-reports.ts +255 -0
  182. package/src/session-mcp-approval.ts +30 -0
  183. package/src/session-message-search.ts +348 -0
  184. package/src/session-queue-commands.ts +60 -36
  185. package/src/session-retry.ts +319 -0
  186. package/src/skill-config-migration.ts +11 -2
  187. package/src/skill-source-release-impact.ts +33 -0
  188. package/src/skill-source-release.ts +67 -26
  189. package/src/skills.ts +44 -6
  190. package/src/user-resource-authority.ts +0 -56
  191. package/dist/chunk-6T2GGYWE.js.map +0 -1
  192. package/dist/chunk-7HMK3B45.js.map +0 -1
  193. package/dist/chunk-DPONPYJD.js.map +0 -1
  194. package/dist/chunk-F73UKJY6.js.map +0 -1
  195. package/dist/chunk-T6YLHJLS.js.map +0 -1
  196. package/dist/host-mcp-binding-match.d.ts +0 -5
  197. package/dist/host-mcp-bindings.d.ts +0 -79
  198. package/dist/host-mcp-resolvers.d.ts +0 -47
  199. package/dist/host-mcp-task-authority.d.ts +0 -43
  200. package/dist/host-selection-identity.d.ts +0 -6
  201. package/dist/pack-components.d.ts +0 -83
  202. package/dist/pack-installations.d.ts +0 -86
  203. package/src/host-mcp-binding-match.ts +0 -34
  204. package/src/host-mcp-bindings.ts +0 -777
  205. package/src/host-mcp-resolvers.ts +0 -266
  206. package/src/host-mcp-task-authority.ts +0 -384
  207. package/src/host-selection-identity.ts +0 -29
  208. package/src/pack-components.ts +0 -980
  209. package/src/pack-installations.ts +0 -950
  210. /package/dist/{chunk-MWMXWHJD.js.map → chunk-7JCDSMX2.js.map} +0 -0
  211. /package/dist/{chunk-ERKVSPDX.js.map → chunk-LVY33EWL.js.map} +0 -0
  212. /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();