@opengeni/db 1.0.1 → 3.2.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/dist/attached-browser-devices.d.ts +7 -0
  2. package/dist/automations.d.ts +154 -0
  3. package/dist/canonical-human-identities.js +3 -3
  4. package/dist/capability-integrations.d.ts +13 -0
  5. package/dist/child-lifecycle-notices.d.ts +68 -0
  6. package/dist/{chunk-4SR4HKKA.js → chunk-2EH4WRHL.js} +19 -11
  7. package/dist/chunk-2EH4WRHL.js.map +1 -0
  8. package/dist/{chunk-CUPM3FGB.js → chunk-2PRS4J5Q.js} +3 -3
  9. package/dist/chunk-6HMM2R5T.js +390 -0
  10. package/dist/chunk-6HMM2R5T.js.map +1 -0
  11. package/dist/{chunk-ZWREYEWA.js → chunk-AZHW7R5V.js} +56 -11
  12. package/dist/chunk-AZHW7R5V.js.map +1 -0
  13. package/dist/{chunk-LTG3S6ZY.js → chunk-E6CLMKEE.js} +2 -2
  14. package/dist/{chunk-6ZYKCRFO.js → chunk-KRLPEMLH.js} +7591 -4417
  15. package/dist/chunk-KRLPEMLH.js.map +1 -0
  16. package/dist/chunk-NAB6FKFN.js +516 -0
  17. package/dist/chunk-NAB6FKFN.js.map +1 -0
  18. package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
  19. package/dist/chunk-SIBYBA2D.js.map +1 -0
  20. package/dist/chunk-UZNXG44K.js +4508 -0
  21. package/dist/chunk-UZNXG44K.js.map +1 -0
  22. package/dist/codemode-operations.d.ts +1 -6
  23. package/dist/company-brain-context-selection-schema.d.ts +705 -0
  24. package/dist/company-brain-context-selection.d.ts +94 -0
  25. package/dist/company-brain-governed-writes.d.ts +24 -0
  26. package/dist/company-brain.d.ts +55 -0
  27. package/dist/company-profile-agent-admin.d.ts +15 -0
  28. package/dist/company-profile-schema.d.ts +619 -2
  29. package/dist/company-profile.d.ts +9 -4
  30. package/dist/connection-authority.d.ts +95 -0
  31. package/dist/connection-scopes.d.ts +8 -0
  32. package/dist/connection-tenancy-backfill.d.ts +85 -0
  33. package/dist/connection-token-resolver.d.ts +30 -1
  34. package/dist/database.d.ts +23 -4
  35. package/dist/editable-artifact-durable-export.js +2 -2
  36. package/dist/editable-artifacts.d.ts +2 -2
  37. package/dist/editable-artifacts.js +3 -3
  38. package/dist/governed-learning-activation-schema.d.ts +984 -0
  39. package/dist/governed-learning-activation.d.ts +98 -0
  40. package/dist/governed-learning-evaluator-schema.d.ts +633 -0
  41. package/dist/governed-learning-evaluator.d.ts +28 -0
  42. package/dist/index.d.ts +1606 -81
  43. package/dist/index.js +21898 -8147
  44. package/dist/index.js.map +1 -1
  45. package/dist/insights.d.ts +18 -0
  46. package/dist/integration-bindings.d.ts +1 -0
  47. package/dist/integration-facets.d.ts +30 -0
  48. package/dist/interaction-placement-loss.d.ts +22 -0
  49. package/dist/managed-human-provisioning.d.ts +25 -0
  50. package/dist/memory-domain.d.ts +3 -3
  51. package/dist/migrate.d.ts +11 -1
  52. package/dist/migrate.js +1 -1
  53. package/dist/new-session-drafts.d.ts +43 -5
  54. package/dist/organization-membership-backfill.d.ts +174 -0
  55. package/dist/organization-membership-lifecycle.d.ts +162 -0
  56. package/dist/pr-review.d.ts +107 -0
  57. package/dist/preference-registry-schema.d.ts +2 -0
  58. package/dist/preference-registry.d.ts +2 -0
  59. package/dist/provision-roles.js +1 -1
  60. package/dist/runtime-posture.d.ts +29 -8
  61. package/dist/schema.d.ts +24914 -13703
  62. package/dist/schema.js +99 -1
  63. package/dist/scoped-knowledge-schema.d.ts +52 -1
  64. package/dist/session-background-commands.d.ts +125 -0
  65. package/dist/session-background-commands.js +34 -0
  66. package/dist/session-background-commands.js.map +1 -0
  67. package/dist/session-control.d.ts +156 -3
  68. package/dist/session-goal-pacing.d.ts +68 -0
  69. package/dist/session-queue-commands.d.ts +43 -4
  70. package/dist/session-realtime-ledger.d.ts +2 -0
  71. package/dist/session-tenancy.d.ts +71 -1
  72. package/dist/session-tenancy.js +25 -3
  73. package/dist/session-tool-call-settlement.d.ts +9 -4
  74. package/dist/slack-routing-personal-workspace.d.ts +23 -0
  75. package/dist/slack-routing.d.ts +268 -0
  76. package/dist/slack-task-policy-schema.d.ts +34 -0
  77. package/dist/slack-task-policy.d.ts +8 -0
  78. package/dist/slack-user-link-access.d.ts +28 -0
  79. package/dist/task-notes-schema.d.ts +594 -0
  80. package/dist/task-notes.d.ts +39 -0
  81. package/dist/turn-initiator.d.ts +2 -0
  82. package/dist/user-resource-authority.d.ts +109 -0
  83. package/dist/video-generation.js +3 -3
  84. package/dist/workspace-authority.d.ts +81 -0
  85. package/dist/workspace-instruction-policies-schema.d.ts +279 -0
  86. package/dist/workspace-instruction-policies.d.ts +62 -0
  87. package/dist/workspace-learning-policy.d.ts +1 -0
  88. package/dist/workspace-membership-permissions.d.ts +2 -0
  89. package/drizzle/0240_enrollment_connection_authority.sql +39 -0
  90. package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
  91. package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
  92. package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
  93. package/drizzle/0245_model_context_contribution_facts.sql +88 -0
  94. package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
  95. package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
  96. package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
  97. package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
  98. package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
  99. package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
  100. package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
  101. package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
  102. package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
  103. package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
  104. package/drizzle/0256_connection_authority_delegation.sql +703 -0
  105. package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
  106. package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
  107. package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
  108. package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
  109. package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
  110. package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
  111. package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
  112. package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
  113. package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
  114. package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
  115. package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
  116. package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
  117. package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
  118. package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
  119. package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
  120. package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
  121. package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
  122. package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
  123. package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
  124. package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
  125. package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
  126. package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
  127. package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
  128. package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
  129. package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
  130. package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
  131. package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
  132. package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
  133. package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
  134. package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
  135. package/drizzle/0289_session_composer_policy_authority.sql +597 -0
  136. package/drizzle/0290_organization_membership_backfill.sql +268 -0
  137. package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
  138. package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
  139. package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
  140. package/drizzle/0294_preference_activation_authority.sql +237 -0
  141. package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
  142. package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
  143. package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
  144. package/drizzle/0298_organization_tenancy_parity.sql +609 -0
  145. package/drizzle/0299_organization_membership_lock_order.sql +291 -0
  146. package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
  147. package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
  148. package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
  149. package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
  150. package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
  151. package/drizzle/0305_personal_resource_grant_management.sql +584 -0
  152. package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
  153. package/drizzle/0307_session_attention_state.sql +51 -0
  154. package/drizzle/0308_session_archives.sql +35 -0
  155. package/drizzle/0309_channel_project_pins.sql +6 -0
  156. package/drizzle/0310_channel_project_order.sql +27 -0
  157. package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
  158. package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
  159. package/drizzle/0313_private_child_session_authority.sql +365 -0
  160. package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
  161. package/drizzle/0315_personal_github_repository_selection.sql +624 -0
  162. package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
  163. package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
  164. package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
  165. package/drizzle/0319_event_triggered_automations.sql +254 -0
  166. package/drizzle/0320_pr_review_pack.sql +113 -0
  167. package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
  168. package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
  169. package/drizzle/0323_organization_private_session_enablement.sql +489 -0
  170. package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
  171. package/drizzle/0325_child_lifecycle_notices.sql +44 -0
  172. package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
  173. package/drizzle/0327_slack_first_task_hint.sql +29 -0
  174. package/drizzle/0328_session_background_commands.sql +590 -0
  175. package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
  176. package/drizzle/0330_api_key_descriptions.sql +20 -0
  177. package/drizzle/0331_managed_organization_creation.sql +163 -0
  178. package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
  179. package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
  180. package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
  181. package/drizzle/0335_slack_workspace_routing.sql +460 -0
  182. package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
  183. package/drizzle/0337_slack_routed_action_handles.sql +189 -0
  184. package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
  185. package/drizzle/0339_document_authority_reclassification.sql +1153 -0
  186. package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
  187. package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
  188. package/drizzle/0342_slack_route_prompt_single_pending.sql +32 -0
  189. package/drizzle/0343_personal_document_force_rls_lock_repair.sql +146 -0
  190. package/drizzle/0344_private_session_visibility_transition_gate.sql +85 -0
  191. package/drizzle/0345_tenant_scoped_session_tenancy_fence.sql +2839 -0
  192. package/drizzle/0346_document_migration_audit_surface.sql +454 -0
  193. package/drizzle/0347_connection_authority_convergence_evidence.sql +396 -0
  194. package/package.json +10 -6
  195. package/src/attached-browser-devices.ts +233 -5
  196. package/src/automations.ts +911 -0
  197. package/src/browser-sessions.ts +10 -0
  198. package/src/capability-integrations.ts +175 -1
  199. package/src/child-lifecycle-notices.ts +241 -0
  200. package/src/codemode-operations.ts +1 -35
  201. package/src/company-brain-context-selection-schema.ts +100 -0
  202. package/src/company-brain-context-selection.ts +359 -0
  203. package/src/company-brain-governed-writes.ts +1170 -0
  204. package/src/company-brain.ts +309 -0
  205. package/src/company-profile-agent-admin.ts +225 -0
  206. package/src/company-profile-schema.ts +121 -1
  207. package/src/company-profile.ts +21 -0
  208. package/src/computer-sessions.ts +10 -1
  209. package/src/connection-authority.ts +290 -0
  210. package/src/connection-scopes.ts +20 -0
  211. package/src/connection-tenancy-backfill.ts +167 -0
  212. package/src/connection-token-resolver.ts +263 -31
  213. package/src/database.ts +87 -2
  214. package/src/editable-artifacts-schema.ts +10 -5
  215. package/src/editable-artifacts.ts +21 -11
  216. package/src/governed-learning-activation-schema.ts +121 -0
  217. package/src/governed-learning-activation.ts +588 -0
  218. package/src/governed-learning-evaluator-schema.ts +72 -0
  219. package/src/governed-learning-evaluator.ts +217 -0
  220. package/src/index.ts +11676 -1550
  221. package/src/insights.ts +107 -0
  222. package/src/integration-bindings.ts +88 -6
  223. package/src/integration-facets.ts +287 -84
  224. package/src/interaction-placement-loss.ts +234 -0
  225. package/src/managed-human-provisioning.ts +116 -0
  226. package/src/memory-domain.ts +27 -32
  227. package/src/migrate.ts +77 -1
  228. package/src/new-session-drafts.ts +229 -35
  229. package/src/organization-membership-backfill.ts +735 -0
  230. package/src/organization-membership-lifecycle.ts +995 -0
  231. package/src/pr-review.ts +890 -0
  232. package/src/preference-registry.ts +31 -1
  233. package/src/provision-roles.ts +905 -6
  234. package/src/runtime-posture-cli.ts +2 -0
  235. package/src/runtime-posture.ts +1317 -72
  236. package/src/schema.ts +3316 -428
  237. package/src/scoped-knowledge-schema.ts +16 -8
  238. package/src/scoped-knowledge.ts +1 -1
  239. package/src/session-background-commands.ts +759 -0
  240. package/src/session-control.ts +640 -74
  241. package/src/session-goal-pacing.ts +126 -0
  242. package/src/session-queue-commands.ts +441 -85
  243. package/src/session-realtime-context.ts +4 -6
  244. package/src/session-realtime-ledger.ts +28 -14
  245. package/src/session-realtime-mirror.ts +1 -1
  246. package/src/session-tenancy.ts +410 -49
  247. package/src/session-tool-call-settlement.ts +38 -29
  248. package/src/slack-routing-personal-workspace.ts +67 -0
  249. package/src/slack-routing.ts +867 -0
  250. package/src/slack-task-policy-schema.ts +5 -0
  251. package/src/slack-task-policy.ts +18 -0
  252. package/src/slack-user-link-access.ts +138 -11
  253. package/src/task-notes-schema.ts +74 -0
  254. package/src/task-notes.ts +124 -2
  255. package/src/turn-initiator.ts +8 -0
  256. package/src/user-resource-authority.ts +377 -0
  257. package/src/workspace-authority.ts +160 -0
  258. package/src/workspace-instruction-policies-schema.ts +53 -1
  259. package/src/workspace-instruction-policies.ts +528 -77
  260. package/src/workspace-learning-policy.ts +21 -5
  261. package/src/workspace-membership-permissions.ts +9 -0
  262. package/dist/chunk-4SR4HKKA.js.map +0 -1
  263. package/dist/chunk-6ZYKCRFO.js.map +0 -1
  264. package/dist/chunk-NXAFLCQX.js +0 -2468
  265. package/dist/chunk-NXAFLCQX.js.map +0 -1
  266. package/dist/chunk-SK7YYHBI.js.map +0 -1
  267. package/dist/chunk-WD5ROSLL.js +0 -117
  268. package/dist/chunk-WD5ROSLL.js.map +0 -1
  269. package/dist/chunk-ZWREYEWA.js.map +0 -1
  270. /package/dist/{chunk-CUPM3FGB.js.map → chunk-2PRS4J5Q.js.map} +0 -0
  271. /package/dist/{chunk-LTG3S6ZY.js.map → chunk-E6CLMKEE.js.map} +0 -0
@@ -0,0 +1,1084 @@
1
+ -- deployment-mode: maintenance
2
+ -- This is a drained runtime cutover. Old workers neither materialize accepted
3
+ -- connection authority nor carry exact attempt/use fences and must not restart.
4
+
5
+ SET LOCAL lock_timeout = '5s';
6
+ SET LOCAL statement_timeout = '10min';
7
+
8
+ DO $connection_authority_writer_drain_before_lock$
9
+ BEGIN
10
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app')
11
+ AND EXISTS (
12
+ SELECT 1 FROM pg_stat_activity
13
+ WHERE datname = current_database()
14
+ AND usename = 'opengeni_app'
15
+ AND pid <> pg_backend_pid()
16
+ )
17
+ THEN
18
+ RAISE EXCEPTION
19
+ '0264 connection authority activation requires all opengeni_app sessions to be stopped'
20
+ USING ERRCODE = '55000';
21
+ END IF;
22
+ END
23
+ $connection_authority_writer_drain_before_lock$;
24
+
25
+ LOCK TABLE sessions IN ACCESS EXCLUSIVE MODE;
26
+ LOCK TABLE session_turns IN ACCESS EXCLUSIVE MODE;
27
+ LOCK TABLE session_system_updates IN ACCESS EXCLUSIVE MODE;
28
+ LOCK TABLE session_system_update_outbox IN ACCESS EXCLUSIVE MODE;
29
+ LOCK TABLE scheduled_tasks IN ACCESS EXCLUSIVE MODE;
30
+ LOCK TABLE connections IN ACCESS EXCLUSIVE MODE;
31
+
32
+ DO $connection_authority_writer_drain_after_lock$
33
+ BEGIN
34
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app')
35
+ AND EXISTS (
36
+ SELECT 1 FROM pg_stat_activity
37
+ WHERE datname = current_database()
38
+ AND usename = 'opengeni_app'
39
+ AND pid <> pg_backend_pid()
40
+ )
41
+ THEN
42
+ RAISE EXCEPTION
43
+ '0264 connection authority activation requires all opengeni_app sessions to be stopped'
44
+ USING ERRCODE = '55000';
45
+ END IF;
46
+ END
47
+ $connection_authority_writer_drain_after_lock$;
48
+
49
+ -- The maintenance label is operational guidance; this data precondition is
50
+ -- the durable cutover fence. Migration 0256 activated common-user Connection
51
+ -- rows before accepted-work snapshots existed. Do not install the new runtime
52
+ -- while any executable frozen source could still carry one of those rows by
53
+ -- the legacy owner tuple without an explicit grant.
54
+ DO $accepted_work_drain$
55
+ BEGIN
56
+ IF EXISTS (
57
+ SELECT 1
58
+ FROM sessions session_value
59
+ CROSS JOIN LATERAL jsonb_array_elements(
60
+ session_value.initial_personal_connection_delegations
61
+ ) delegation
62
+ JOIN connections connection_value
63
+ ON connection_value.id::text = delegation ->> 'connectionId'
64
+ AND connection_value.account_id = session_value.account_id
65
+ WHERE session_value.status <> 'cancelled'
66
+ AND NOT EXISTS (
67
+ SELECT 1 FROM session_turns existing_turn
68
+ WHERE existing_turn.workspace_id = session_value.workspace_id
69
+ AND existing_turn.session_id = session_value.id
70
+ )
71
+ AND connection_value.authority_scope = 'user'
72
+ ) OR EXISTS (
73
+ SELECT 1
74
+ FROM session_turns turn_value
75
+ CROSS JOIN LATERAL jsonb_array_elements(
76
+ turn_value.personal_connection_delegations
77
+ ) delegation
78
+ JOIN connections connection_value
79
+ ON connection_value.id::text = delegation ->> 'connectionId'
80
+ AND connection_value.account_id = turn_value.account_id
81
+ WHERE turn_value.status IN (
82
+ 'queued', 'running', 'requires_action', 'recovering', 'waiting_capacity'
83
+ )
84
+ AND connection_value.authority_scope = 'user'
85
+ ) OR EXISTS (
86
+ SELECT 1
87
+ FROM session_system_updates update_value
88
+ CROSS JOIN LATERAL jsonb_array_elements(
89
+ update_value.personal_connection_delegations
90
+ ) delegation
91
+ JOIN connections connection_value
92
+ ON connection_value.id::text = delegation ->> 'connectionId'
93
+ AND connection_value.account_id = update_value.account_id
94
+ WHERE update_value.state = 'pending'
95
+ AND connection_value.authority_scope = 'user'
96
+ ) OR EXISTS (
97
+ SELECT 1
98
+ FROM session_system_update_outbox outbox
99
+ CROSS JOIN LATERAL jsonb_array_elements(
100
+ outbox.personal_connection_delegations
101
+ ) delegation
102
+ JOIN connections connection_value
103
+ ON connection_value.id::text = delegation ->> 'connectionId'
104
+ AND connection_value.account_id = outbox.account_id
105
+ WHERE outbox.status = 'pending'
106
+ AND connection_value.authority_scope = 'user'
107
+ ) OR EXISTS (
108
+ SELECT 1
109
+ FROM scheduled_tasks task
110
+ CROSS JOIN LATERAL jsonb_array_elements(
111
+ task.personal_connection_delegations
112
+ ) delegation
113
+ JOIN connections connection_value
114
+ ON connection_value.id::text = delegation ->> 'connectionId'
115
+ AND connection_value.account_id = task.account_id
116
+ WHERE task.status = 'active'
117
+ AND connection_value.authority_scope = 'user'
118
+ ) THEN
119
+ RAISE EXCEPTION
120
+ '0264 requires draining or superseding executable pre-activation common-user connection work'
121
+ USING ERRCODE = '55000';
122
+ END IF;
123
+ END
124
+ $accepted_work_drain$;
125
+
126
+ CREATE TABLE "turn_connection_authority_snapshots" (
127
+ "account_id" uuid NOT NULL,
128
+ "workspace_id" uuid NOT NULL,
129
+ "session_id" uuid NOT NULL,
130
+ "turn_id" uuid NOT NULL,
131
+ "server_id" text NOT NULL,
132
+ "connection_id" uuid NOT NULL,
133
+ "connection_generation" bigint NOT NULL,
134
+ "origin_workspace_id" uuid NOT NULL,
135
+ "provider_domain" text NOT NULL,
136
+ "connection_kind" text NOT NULL,
137
+ "authority_scope" text NOT NULL,
138
+ "authority_source" text NOT NULL,
139
+ "owner_subject_id" text,
140
+ "owner_organization_membership_id" uuid,
141
+ "membership_authorization_revision" bigint,
142
+ "authority_id" uuid,
143
+ "authority_generation" bigint,
144
+ "grant_id" uuid,
145
+ "grant_generation" bigint,
146
+ "grant_mode" text,
147
+ "grant_context" text,
148
+ "grant_session_id" uuid,
149
+ "grant_authority_epoch" integer,
150
+ "session_visibility" text NOT NULL,
151
+ "session_authority_epoch" integer NOT NULL,
152
+ "canonical_snapshot" jsonb NOT NULL,
153
+ "snapshot_digest" bytea NOT NULL,
154
+ "created_at" timestamptz NOT NULL DEFAULT clock_timestamp(),
155
+ PRIMARY KEY ("turn_id", "server_id"),
156
+ CONSTRAINT "turn_connection_authority_server_check" CHECK (
157
+ octet_length("server_id") BETWEEN 1 AND 256
158
+ ),
159
+ CONSTRAINT "turn_connection_authority_generation_check" CHECK (
160
+ "connection_generation" > 0
161
+ AND "session_authority_epoch" > 0
162
+ AND ("membership_authorization_revision" IS NULL
163
+ OR "membership_authorization_revision" > 0)
164
+ AND ("authority_generation" IS NULL OR "authority_generation" > 0)
165
+ AND ("grant_generation" IS NULL OR "grant_generation" > 0)
166
+ ),
167
+ CONSTRAINT "turn_connection_authority_grant_check" CHECK (
168
+ ("authority_scope" = 'user'
169
+ AND "authority_source" = 'user_delegation'
170
+ AND "owner_subject_id" IS NOT NULL
171
+ AND "owner_organization_membership_id" IS NOT NULL
172
+ AND "membership_authorization_revision" > 0
173
+ AND "authority_id" IS NOT NULL AND "authority_generation" > 0
174
+ AND "grant_id" IS NOT NULL AND "grant_generation" > 0
175
+ AND "grant_mode" IN ('once', 'session', 'always')
176
+ AND "grant_context" IN ('user_private', 'workspace_shared')
177
+ AND (("grant_mode" = 'always' AND "grant_session_id" IS NULL
178
+ AND "grant_authority_epoch" IS NULL)
179
+ OR ("grant_mode" IN ('once', 'session') AND "grant_session_id" = "session_id"
180
+ AND "grant_authority_epoch" > 0)))
181
+ OR ("authority_scope" = 'workspace'
182
+ AND "authority_source" IN ('explicit_workspace', 'legacy_workspace_omission')
183
+ AND "owner_subject_id" IS NULL
184
+ AND "owner_organization_membership_id" IS NULL
185
+ AND "membership_authorization_revision" IS NULL
186
+ AND "authority_id" IS NULL AND "authority_generation" IS NULL
187
+ AND "grant_id" IS NULL AND "grant_generation" IS NULL
188
+ AND "grant_mode" IS NULL AND "grant_context" IS NULL
189
+ AND "grant_session_id" IS NULL AND "grant_authority_epoch" IS NULL)
190
+ OR ("authority_scope" = 'legacy_user'
191
+ AND "authority_source" = 'legacy_user_compatibility'
192
+ AND "owner_subject_id" IS NOT NULL
193
+ AND "owner_organization_membership_id" IS NULL
194
+ AND "membership_authorization_revision" IS NULL
195
+ AND "authority_id" IS NULL AND "authority_generation" IS NULL
196
+ AND "grant_id" IS NULL AND "grant_generation" IS NULL
197
+ AND "grant_mode" IS NULL AND "grant_context" IS NULL
198
+ AND "grant_session_id" IS NULL AND "grant_authority_epoch" IS NULL)
199
+ ),
200
+ CONSTRAINT "turn_connection_authority_turn_fk" FOREIGN KEY (
201
+ "workspace_id", "turn_id"
202
+ ) REFERENCES "session_turns"("workspace_id", "id") ON DELETE CASCADE,
203
+ CONSTRAINT "turn_connection_authority_connection_fk" FOREIGN KEY ("connection_id")
204
+ REFERENCES "connections"("id") ON DELETE RESTRICT,
205
+ CONSTRAINT "turn_connection_authority_membership_fk" FOREIGN KEY (
206
+ "owner_organization_membership_id", "account_id"
207
+ ) REFERENCES "organization_memberships"("id", "account_id") ON DELETE RESTRICT,
208
+ CONSTRAINT "turn_connection_authority_authority_fk" FOREIGN KEY (
209
+ "authority_id", "account_id"
210
+ ) REFERENCES "organization_user_resource_authorities"("id", "account_id")
211
+ ON DELETE RESTRICT,
212
+ CONSTRAINT "turn_connection_authority_grant_fk" FOREIGN KEY (
213
+ "grant_id", "account_id"
214
+ ) REFERENCES "organization_user_resource_grants"("id", "account_id") ON DELETE RESTRICT
215
+ );
216
+
217
+ CREATE INDEX "turn_connection_authority_connection_idx"
218
+ ON "turn_connection_authority_snapshots" (
219
+ "account_id", "connection_id", "turn_id", "server_id"
220
+ );
221
+
222
+ ALTER TABLE "turn_connection_authority_snapshots" ENABLE ROW LEVEL SECURITY;
223
+ ALTER TABLE "turn_connection_authority_snapshots" FORCE ROW LEVEL SECURITY;
224
+ CREATE POLICY "organization_isolation" ON "turn_connection_authority_snapshots"
225
+ USING (
226
+ "account_id" = nullif(current_setting('opengeni.account_id', true), '')::uuid
227
+ AND "workspace_id" = nullif(current_setting('opengeni.workspace_id', true), '')::uuid
228
+ ) WITH CHECK (
229
+ "account_id" = nullif(current_setting('opengeni.account_id', true), '')::uuid
230
+ AND "workspace_id" = nullif(current_setting('opengeni.workspace_id', true), '')::uuid
231
+ );
232
+
233
+ CREATE TABLE "connection_use_audit_facts" (
234
+ "physical_request_id" uuid PRIMARY KEY,
235
+ "use_phase" text NOT NULL,
236
+ "request_digest" bytea NOT NULL,
237
+ "account_id" uuid NOT NULL,
238
+ "workspace_id" uuid NOT NULL,
239
+ "session_id" uuid NOT NULL,
240
+ "turn_id" uuid NOT NULL,
241
+ "attempt_id" uuid NOT NULL,
242
+ "execution_generation" integer NOT NULL,
243
+ "server_id" text NOT NULL,
244
+ "connection_id" uuid,
245
+ "connection_generation" bigint,
246
+ "authority_scope" text,
247
+ "owner_subject_id" text,
248
+ "authority_id" uuid,
249
+ "grant_id" uuid,
250
+ "outcome" text NOT NULL,
251
+ "denial_reason" text,
252
+ "occurred_at" timestamptz NOT NULL DEFAULT clock_timestamp(),
253
+ CONSTRAINT "connection_use_audit_shape_check" CHECK (
254
+ octet_length("server_id") BETWEEN 1 AND 256
255
+ AND octet_length("request_digest") = 32
256
+ AND "execution_generation" > 0
257
+ AND "use_phase" IN ('credential_resolution', 'provider_request')
258
+ AND "outcome" IN ('authorized', 'denied')
259
+ AND (("outcome" = 'authorized' AND "denial_reason" IS NULL
260
+ AND "connection_id" IS NOT NULL AND "connection_generation" > 0
261
+ AND "authority_scope" IN ('workspace', 'user', 'legacy_user'))
262
+ OR ("outcome" = 'denied' AND octet_length("denial_reason") BETWEEN 1 AND 128))
263
+ )
264
+ );
265
+
266
+ ALTER TABLE "connection_use_audit_facts" ENABLE ROW LEVEL SECURITY;
267
+ ALTER TABLE "connection_use_audit_facts" FORCE ROW LEVEL SECURITY;
268
+ CREATE POLICY "organization_isolation" ON "connection_use_audit_facts"
269
+ USING (
270
+ "account_id" = nullif(current_setting('opengeni.account_id', true), '')::uuid
271
+ AND "workspace_id" = nullif(current_setting('opengeni.workspace_id', true), '')::uuid
272
+ ) WITH CHECK (
273
+ "account_id" = nullif(current_setting('opengeni.account_id', true), '')::uuid
274
+ AND "workspace_id" = nullif(current_setting('opengeni.workspace_id', true), '')::uuid
275
+ );
276
+
277
+ CREATE INDEX "connection_use_audit_attempt_idx"
278
+ ON "connection_use_audit_facts" ("workspace_id", "attempt_id", "occurred_at");
279
+
280
+ DO $connection_authority_runtime$
281
+ DECLARE
282
+ data_schema text := current_schema();
283
+ BEGIN
284
+ EXECUTE format($ddl$
285
+ CREATE OR REPLACE FUNCTION %1$I.resolve_personal_connection_authority_selection(
286
+ p_account_id uuid, p_target_workspace_id uuid, p_subject_id text,
287
+ p_connection_id uuid, p_delegation jsonb
288
+ ) RETURNS TABLE (origin_workspace_id uuid)
289
+ LANGUAGE plpgsql
290
+ SECURITY DEFINER
291
+ SET search_path = %1$I, pg_catalog, pg_temp
292
+ AS $body$
293
+ DECLARE
294
+ membership_row organization_memberships%%ROWTYPE;
295
+ connection_row connections%%ROWTYPE;
296
+ authority_row organization_user_resource_authorities%%ROWTYPE;
297
+ grant_row organization_user_resource_grants%%ROWTYPE;
298
+ BEGIN
299
+ IF p_account_id IS NULL OR p_target_workspace_id IS NULL
300
+ OR p_connection_id IS NULL OR nullif(btrim(p_subject_id), '') IS NULL
301
+ OR nullif(current_setting('opengeni.account_id', true), '')::uuid
302
+ IS DISTINCT FROM p_account_id
303
+ OR nullif(current_setting('opengeni.workspace_id', true), '')::uuid
304
+ IS DISTINCT FROM p_target_workspace_id
305
+ OR nullif(current_setting('opengeni.subject_id', true), '')
306
+ IS DISTINCT FROM p_subject_id
307
+ OR p_delegation ->> 'organizationId' IS DISTINCT FROM p_account_id::text
308
+ OR p_delegation ->> 'workspaceId' IS DISTINCT FROM p_target_workspace_id::text
309
+ OR p_delegation ->> 'action' IS DISTINCT FROM 'connection.use'
310
+ THEN
311
+ RAISE EXCEPTION 'personal connection authority selection context is invalid'
312
+ USING ERRCODE = '42501';
313
+ END IF;
314
+
315
+ PERFORM 1 FROM workspaces workspace_value
316
+ WHERE workspace_value.id = p_target_workspace_id
317
+ AND workspace_value.account_id = p_account_id
318
+ FOR SHARE;
319
+ IF NOT FOUND THEN
320
+ RAISE EXCEPTION 'connection authority target workspace is unavailable'
321
+ USING ERRCODE = '42501';
322
+ END IF;
323
+
324
+ SELECT membership.* INTO STRICT membership_row
325
+ FROM organization_memberships membership
326
+ WHERE membership.account_id = p_account_id
327
+ AND membership.subject_id = p_subject_id
328
+ AND membership.status = 'active'
329
+ AND membership.revoked_at IS NULL
330
+ FOR SHARE;
331
+ IF membership_row.personal_workspace_id IS DISTINCT FROM p_target_workspace_id
332
+ AND NOT EXISTS (
333
+ SELECT 1 FROM workspace_memberships workspace_membership
334
+ WHERE workspace_membership.account_id = p_account_id
335
+ AND workspace_membership.workspace_id = p_target_workspace_id
336
+ AND workspace_membership.subject_id = p_subject_id
337
+ )
338
+ THEN
339
+ RAISE EXCEPTION 'personal connection owner lacks target workspace access'
340
+ USING ERRCODE = '42501';
341
+ END IF;
342
+
343
+ SELECT connection_value.* INTO STRICT connection_row
344
+ FROM connections connection_value
345
+ WHERE connection_value.id = p_connection_id
346
+ AND connection_value.account_id = p_account_id
347
+ AND connection_value.subject_id = p_subject_id
348
+ AND connection_value.owner_organization_membership_id = membership_row.id
349
+ AND connection_value.authority_scope = 'user'
350
+ AND connection_value.status = 'active'
351
+ FOR SHARE;
352
+
353
+ SELECT authority.* INTO STRICT authority_row
354
+ FROM organization_user_resource_authorities authority
355
+ WHERE authority.id = nullif(p_delegation ->> 'authorityId', '')::uuid
356
+ AND authority.account_id = p_account_id
357
+ AND authority.organization_membership_id = membership_row.id
358
+ AND authority.resource_kind = 'connection'
359
+ AND authority.resource_id = connection_row.id
360
+ AND authority.origin_workspace_id = connection_row.origin_workspace_id
361
+ AND authority.generation = (p_delegation ->> 'authorityGeneration')::bigint
362
+ AND authority.status = 'active'
363
+ AND authority.revoked_at IS NULL
364
+ FOR SHARE;
365
+ IF connection_row.authority_id IS DISTINCT FROM authority_row.id THEN
366
+ RAISE EXCEPTION 'personal connection authority selection is unavailable'
367
+ USING ERRCODE = '42501';
368
+ END IF;
369
+
370
+ SELECT grant_value.* INTO STRICT grant_row
371
+ FROM organization_user_resource_grants grant_value
372
+ WHERE grant_value.id = nullif(p_delegation ->> 'grantId', '')::uuid
373
+ AND grant_value.account_id = p_account_id
374
+ AND grant_value.authority_id = authority_row.id
375
+ AND grant_value.owner_organization_membership_id = membership_row.id
376
+ AND grant_value.workspace_id = p_target_workspace_id
377
+ AND grant_value.action = 'connection.use'
378
+ AND grant_value.mode = p_delegation ->> 'mode'
379
+ AND grant_value.context = p_delegation ->> 'context'
380
+ AND grant_value.generation = (p_delegation ->> 'grantGeneration')::bigint
381
+ AND (
382
+ (grant_value.status = 'active'
383
+ AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp()))
384
+ OR (grant_value.status = 'consumed' AND grant_value.mode = 'once' AND EXISTS (
385
+ SELECT 1 FROM connection_use_once_consumption_receipts receipt
386
+ WHERE receipt.grant_id = grant_value.id
387
+ AND receipt.account_id = grant_value.account_id
388
+ AND receipt.authority_id = grant_value.authority_id
389
+ AND receipt.authority_generation = authority_row.generation
390
+ AND receipt.grant_generation = grant_value.generation
391
+ AND receipt.accepted_work_kind = 'turn'
392
+ ))
393
+ )
394
+ AND nullif(p_delegation ->> 'sessionId', '')::uuid
395
+ IS NOT DISTINCT FROM grant_value.session_id
396
+ AND nullif(p_delegation ->> 'authorityEpoch', '')::integer
397
+ IS NOT DISTINCT FROM grant_value.authority_epoch
398
+ FOR SHARE;
399
+
400
+ origin_workspace_id := connection_row.origin_workspace_id;
401
+ RETURN NEXT;
402
+ END
403
+ $body$;
404
+
405
+ CREATE OR REPLACE FUNCTION opengeni_private.capture_accepted_turn_connection_authorities()
406
+ RETURNS trigger
407
+ LANGUAGE plpgsql
408
+ SECURITY DEFINER
409
+ SET search_path = %1$I, pg_catalog, public, pg_temp
410
+ AS $body$
411
+ DECLARE
412
+ session_row sessions%%ROWTYPE;
413
+ item jsonb;
414
+ delegation jsonb;
415
+ connection_row connections%%ROWTYPE;
416
+ membership_row organization_memberships%%ROWTYPE;
417
+ authority_row organization_user_resource_authorities%%ROWTYPE;
418
+ grant_row organization_user_resource_grants%%ROWTYPE;
419
+ canonical jsonb;
420
+ existing_digest bytea;
421
+ existing_snapshot jsonb;
422
+ initiating_subject text;
423
+ verified_causal_subject text := coalesce(
424
+ nullif(current_setting('opengeni.initiating_human_subject_id', true), ''),
425
+ nullif(current_setting('opengeni.subject_id', true), '')
426
+ );
427
+ BEGIN
428
+ IF TG_OP = 'UPDATE' THEN
429
+ IF NEW.personal_connection_delegations IS NOT DISTINCT FROM OLD.personal_connection_delegations
430
+ THEN RETURN NEW;
431
+ END IF;
432
+ IF NEW.status <> 'queued' OR EXISTS (
433
+ SELECT 1 FROM session_turn_attempts attempt
434
+ WHERE attempt.turn_id = NEW.id AND attempt.workspace_id = NEW.workspace_id
435
+ ) THEN
436
+ RAISE EXCEPTION 'accepted connection authority cannot change after claim'
437
+ USING ERRCODE = '42501';
438
+ END IF;
439
+ DELETE FROM turn_connection_authority_snapshots snapshot
440
+ WHERE snapshot.turn_id = NEW.id AND snapshot.workspace_id = NEW.workspace_id;
441
+ END IF;
442
+ SELECT session_value.* INTO STRICT session_row
443
+ FROM sessions session_value
444
+ WHERE session_value.id = NEW.session_id
445
+ AND session_value.account_id = NEW.account_id
446
+ AND session_value.workspace_id = NEW.workspace_id
447
+ FOR SHARE;
448
+ initiating_subject := coalesce(
449
+ nullif(btrim(NEW.initiating_human_subject_id), ''),
450
+ CASE WHEN NEW.initiator_kind = 'subject'
451
+ THEN nullif(btrim(NEW.initiator_subject_id), '') END
452
+ );
453
+
454
+ FOR item IN
455
+ SELECT value FROM jsonb_array_elements(NEW.personal_connection_delegations)
456
+ LOOP
457
+ -- Social uses a distinct store. It is not activated here and must never
458
+ -- smuggle the common connection-authority envelope past this boundary.
459
+ IF item ->> 'connectionType' = 'social' THEN
460
+ IF item -> 'userDelegation' IS NOT NULL THEN
461
+ RAISE EXCEPTION 'social connection authority is not activated'
462
+ USING ERRCODE = '42501';
463
+ END IF;
464
+ CONTINUE;
465
+ END IF;
466
+ IF nullif(item ->> 'serverId', '') IS NULL
467
+ OR octet_length(item ->> 'serverId') > 256
468
+ OR nullif(item ->> 'connectionId', '') IS NULL
469
+ THEN
470
+ RAISE EXCEPTION 'invalid accepted connection selection' USING ERRCODE = '22023';
471
+ END IF;
472
+ SELECT connection_value.* INTO connection_row
473
+ FROM connections connection_value
474
+ WHERE connection_value.id = (item ->> 'connectionId')::uuid
475
+ AND connection_value.account_id = NEW.account_id
476
+ FOR SHARE;
477
+
478
+ -- First-party Atlassian remains a named successor. Legacy rows retain
479
+ -- their bounded compatibility behavior, but a common `user` row cannot be
480
+ -- relabeled to bypass snapshot, once, and causal-human admission.
481
+ IF item ->> 'connectionType' = 'atlassian' THEN
482
+ IF item -> 'userDelegation' IS NOT NULL
483
+ OR (FOUND AND connection_row.authority_scope = 'user')
484
+ THEN
485
+ RAISE EXCEPTION 'Atlassian connection authority is not activated'
486
+ USING ERRCODE = '42501';
487
+ END IF;
488
+ CONTINUE;
489
+ END IF;
490
+
491
+ IF NOT FOUND THEN
492
+ IF item -> 'userDelegation' IS NULL THEN
493
+ -- Bounded legacy rows are revalidated by the existing credential
494
+ -- resolver. A missing historical row remains unavailable there; it
495
+ -- must not make unrelated turn acceptance fail during the cutover.
496
+ CONTINUE;
497
+ END IF;
498
+ RAISE EXCEPTION 'accepted connection selection is unavailable'
499
+ USING ERRCODE = '42501';
500
+ END IF;
501
+
502
+ IF connection_row.authority_scope = 'legacy_user' AND item -> 'userDelegation' IS NULL THEN
503
+ CONTINUE;
504
+ END IF;
505
+ IF connection_row.authority_scope <> 'user' THEN
506
+ RAISE EXCEPTION 'accepted personal connection scope is unavailable'
507
+ USING ERRCODE = '42501';
508
+ END IF;
509
+ IF initiating_subject IS NULL OR connection_row.subject_id IS DISTINCT FROM initiating_subject
510
+ OR connection_row.status <> 'active'
511
+ OR lower(connection_row.provider_domain) IS DISTINCT FROM lower(item ->> 'providerDomain')
512
+ OR (item ? 'kind' AND connection_row.kind IS DISTINCT FROM item ->> 'kind')
513
+ THEN
514
+ RAISE EXCEPTION 'accepted personal connection identity is unavailable'
515
+ USING ERRCODE = '42501';
516
+ END IF;
517
+ delegation := item -> 'userDelegation';
518
+ IF delegation IS NULL THEN
519
+ RAISE EXCEPTION 'activated personal connection requires an explicit grant'
520
+ USING ERRCODE = '42501';
521
+ END IF;
522
+ IF verified_causal_subject IS DISTINCT FROM initiating_subject THEN
523
+ RAISE EXCEPTION 'accepted connection authority causal human is unverified'
524
+ USING ERRCODE = '42501';
525
+ END IF;
526
+
527
+ SELECT membership.* INTO STRICT membership_row
528
+ FROM organization_memberships membership
529
+ WHERE membership.id = connection_row.owner_organization_membership_id
530
+ AND membership.account_id = NEW.account_id
531
+ AND membership.subject_id = initiating_subject
532
+ AND membership.status = 'active'
533
+ AND membership.revoked_at IS NULL
534
+ FOR SHARE;
535
+ IF membership_row.personal_workspace_id IS DISTINCT FROM NEW.workspace_id
536
+ AND NOT EXISTS (
537
+ SELECT 1 FROM workspace_memberships workspace_membership
538
+ WHERE workspace_membership.account_id = NEW.account_id
539
+ AND workspace_membership.workspace_id = NEW.workspace_id
540
+ AND workspace_membership.subject_id = initiating_subject
541
+ )
542
+ THEN
543
+ RAISE EXCEPTION 'personal connection owner lacks target workspace access'
544
+ USING ERRCODE = '42501';
545
+ END IF;
546
+
547
+ SELECT authority.* INTO STRICT authority_row
548
+ FROM organization_user_resource_authorities authority
549
+ WHERE authority.id = nullif(delegation ->> 'authorityId', '')::uuid
550
+ AND authority.account_id = NEW.account_id
551
+ AND authority.organization_membership_id = membership_row.id
552
+ AND authority.resource_kind = 'connection'
553
+ AND authority.resource_id = connection_row.id
554
+ AND authority.origin_workspace_id = connection_row.origin_workspace_id
555
+ AND authority.generation = (delegation ->> 'authorityGeneration')::bigint
556
+ AND authority.status = 'active'
557
+ AND authority.revoked_at IS NULL
558
+ FOR SHARE;
559
+
560
+ SELECT grant_value.* INTO STRICT grant_row
561
+ FROM organization_user_resource_grants grant_value
562
+ WHERE grant_value.id = nullif(delegation ->> 'grantId', '')::uuid
563
+ AND grant_value.account_id = NEW.account_id
564
+ AND grant_value.authority_id = authority_row.id
565
+ AND grant_value.owner_organization_membership_id = membership_row.id
566
+ AND grant_value.workspace_id = NEW.workspace_id
567
+ AND grant_value.action = 'connection.use'
568
+ AND grant_value.mode = delegation ->> 'mode'
569
+ AND grant_value.context = session_row.visibility
570
+ AND grant_value.generation = (delegation ->> 'grantGeneration')::bigint
571
+ AND grant_value.status IN ('active', 'consumed')
572
+ AND (grant_value.expires_at IS NULL OR grant_value.expires_at > clock_timestamp())
573
+ AND ((grant_value.mode = 'always' AND grant_value.session_id IS NULL
574
+ AND grant_value.authority_epoch IS NULL)
575
+ OR (grant_value.mode IN ('once', 'session')
576
+ AND grant_value.session_id = NEW.session_id
577
+ AND grant_value.authority_epoch = session_row.authority_epoch))
578
+ FOR UPDATE;
579
+ IF grant_row.mode = 'once' THEN
580
+ IF grant_row.status = 'active' THEN
581
+ UPDATE organization_user_resource_grants grant_value
582
+ SET status = 'consumed', updated_at = clock_timestamp()
583
+ WHERE grant_value.id = grant_row.id
584
+ AND grant_value.account_id = NEW.account_id
585
+ AND grant_value.generation = grant_row.generation
586
+ AND grant_value.status = 'active';
587
+ IF NOT FOUND THEN
588
+ RAISE EXCEPTION 'connection use grant is no longer admissible'
589
+ USING ERRCODE = '42501';
590
+ END IF;
591
+ INSERT INTO connection_use_once_consumption_receipts (
592
+ grant_id, account_id, authority_id, authority_generation,
593
+ grant_generation, accepted_work_kind, accepted_work_id
594
+ ) VALUES (
595
+ grant_row.id, NEW.account_id, authority_row.id,
596
+ authority_row.generation, grant_row.generation, 'turn', NEW.id
597
+ ) ON CONFLICT (grant_id) DO NOTHING;
598
+ IF NOT FOUND THEN
599
+ RAISE EXCEPTION 'connection use grant is already bound to other work'
600
+ USING ERRCODE = '42501';
601
+ END IF;
602
+ grant_row.status := 'consumed';
603
+ ELSIF grant_row.status <> 'consumed' OR NOT EXISTS (
604
+ SELECT 1 FROM connection_use_once_consumption_receipts receipt
605
+ WHERE receipt.grant_id = grant_row.id
606
+ AND receipt.accepted_work_kind = 'turn'
607
+ AND receipt.accepted_work_id = NEW.id
608
+ ) THEN
609
+ RAISE EXCEPTION 'connection use grant is no longer admissible'
610
+ USING ERRCODE = '42501';
611
+ END IF;
612
+ ELSIF grant_row.status <> 'active' THEN
613
+ RAISE EXCEPTION 'connection use grant is no longer admissible' USING ERRCODE = '42501';
614
+ END IF;
615
+ IF delegation ->> 'organizationId' IS DISTINCT FROM NEW.account_id::text
616
+ OR delegation ->> 'workspaceId' IS DISTINCT FROM NEW.workspace_id::text
617
+ OR delegation ->> 'action' IS DISTINCT FROM 'connection.use'
618
+ OR delegation ->> 'context' IS DISTINCT FROM session_row.visibility
619
+ OR nullif(delegation ->> 'sessionId', '')::uuid IS DISTINCT FROM grant_row.session_id
620
+ OR nullif(delegation ->> 'authorityEpoch', '')::integer
621
+ IS DISTINCT FROM grant_row.authority_epoch
622
+ THEN
623
+ RAISE EXCEPTION 'accepted connection grant metadata is contradictory'
624
+ USING ERRCODE = '42501';
625
+ END IF;
626
+ IF nullif(item ->> 'originWorkspaceId', '')::uuid
627
+ IS DISTINCT FROM connection_row.origin_workspace_id
628
+ THEN
629
+ RAISE EXCEPTION 'accepted connection origin is not server-resolved'
630
+ USING ERRCODE = '42501';
631
+ END IF;
632
+
633
+ canonical := jsonb_build_object(
634
+ 'organizationId', NEW.account_id,
635
+ 'originWorkspaceId', connection_row.origin_workspace_id,
636
+ 'targetWorkspaceId', NEW.workspace_id,
637
+ 'targetSessionId', NEW.session_id,
638
+ 'targetSessionVisibility', session_row.visibility,
639
+ 'targetSessionAuthorityEpoch', session_row.authority_epoch,
640
+ 'acceptedWork', jsonb_build_object('kind', 'turn', 'turnId', NEW.id),
641
+ 'connectionId', connection_row.id,
642
+ 'connectionGeneration', connection_row.authority_generation,
643
+ 'connectionStatus', 'active',
644
+ 'providerDomain', lower(connection_row.provider_domain),
645
+ 'connectionKind', connection_row.kind,
646
+ 'scope', 'user',
647
+ 'ownerSubjectId', initiating_subject,
648
+ 'ownerOrganizationMembershipId', membership_row.id,
649
+ 'ownerMembershipAuthorizationRevision', membership_row.authorization_revision,
650
+ 'authoritySource', 'user_delegation',
651
+ 'selectionSources', jsonb_build_array('mcp:' || (item ->> 'serverId')),
652
+ 'userDelegation', jsonb_build_object(
653
+ 'organizationId', NEW.account_id,
654
+ 'authorityId', authority_row.id,
655
+ 'authorityGeneration', authority_row.generation,
656
+ 'workspaceId', NEW.workspace_id,
657
+ 'sessionId', grant_row.session_id,
658
+ 'action', 'connection.use',
659
+ 'mode', grant_row.mode,
660
+ 'context', grant_row.context,
661
+ 'authorityEpoch', grant_row.authority_epoch,
662
+ 'grantId', grant_row.id,
663
+ 'grantGeneration', grant_row.generation
664
+ )
665
+ );
666
+
667
+ SELECT snapshot.snapshot_digest, snapshot.canonical_snapshot
668
+ INTO existing_digest, existing_snapshot
669
+ FROM turn_connection_authority_snapshots snapshot
670
+ WHERE snapshot.turn_id = NEW.id AND snapshot.server_id = item ->> 'serverId';
671
+ IF FOUND THEN
672
+ IF existing_snapshot IS DISTINCT FROM canonical
673
+ OR existing_digest IS DISTINCT FROM digest(convert_to(canonical::text, 'UTF8'), 'sha256')
674
+ THEN
675
+ RAISE EXCEPTION 'accepted connection authority changed across recovery'
676
+ USING ERRCODE = '42501';
677
+ END IF;
678
+ CONTINUE;
679
+ END IF;
680
+
681
+ INSERT INTO turn_connection_authority_snapshots (
682
+ account_id, workspace_id, session_id, turn_id, server_id,
683
+ connection_id, connection_generation, origin_workspace_id,
684
+ provider_domain, connection_kind, authority_scope, authority_source, owner_subject_id,
685
+ owner_organization_membership_id, membership_authorization_revision,
686
+ authority_id, authority_generation, grant_id, grant_generation,
687
+ grant_mode, grant_context, grant_session_id, grant_authority_epoch,
688
+ session_visibility, session_authority_epoch, canonical_snapshot, snapshot_digest
689
+ ) VALUES (
690
+ NEW.account_id, NEW.workspace_id, NEW.session_id, NEW.id, item ->> 'serverId',
691
+ connection_row.id, connection_row.authority_generation,
692
+ connection_row.origin_workspace_id, lower(connection_row.provider_domain),
693
+ connection_row.kind, 'user', 'user_delegation', initiating_subject, membership_row.id,
694
+ membership_row.authorization_revision, authority_row.id, authority_row.generation,
695
+ grant_row.id, grant_row.generation, grant_row.mode, grant_row.context,
696
+ grant_row.session_id, grant_row.authority_epoch,
697
+ session_row.visibility, session_row.authority_epoch, canonical,
698
+ digest(convert_to(canonical::text, 'UTF8'), 'sha256')
699
+ );
700
+ END LOOP;
701
+ RETURN NEW;
702
+ END
703
+ $body$
704
+ $ddl$, data_schema);
705
+
706
+ EXECUTE format($ddl$
707
+ CREATE TRIGGER accepted_turn_connection_authority_capture
708
+ AFTER INSERT OR UPDATE OF personal_connection_delegations ON %1$I.session_turns
709
+ FOR EACH ROW EXECUTE FUNCTION opengeni_private.capture_accepted_turn_connection_authorities()
710
+ $ddl$, data_schema);
711
+
712
+ -- Keep the 0256 ABI only for the bounded workspace/legacy compatibility lane.
713
+ -- A drained old worker that tries to execute an activated common-user snapshot
714
+ -- is denied even though the function signature still exists.
715
+ EXECUTE format(
716
+ 'ALTER FUNCTION %I.resolve_connection_use_authority(uuid, uuid, uuid, jsonb) '
717
+ || 'RENAME TO resolve_connection_use_authority_legacy_0256',
718
+ data_schema
719
+ );
720
+ EXECUTE format($ddl$
721
+ CREATE FUNCTION %1$I.resolve_connection_use_authority(
722
+ p_account_id uuid, p_workspace_id uuid, p_session_id uuid, p_snapshot jsonb
723
+ ) RETURNS TABLE (
724
+ authorization_status text, denial_reason text, connection_id uuid,
725
+ connection_generation bigint, authority_scope text, owner_subject_id text,
726
+ authority_id uuid, grant_id uuid
727
+ )
728
+ LANGUAGE plpgsql
729
+ SECURITY DEFINER
730
+ SET search_path = %1$I, pg_catalog, pg_temp
731
+ AS $body$
732
+ BEGIN
733
+ IF p_snapshot ->> 'scope' = 'user' THEN
734
+ authorization_status := 'denied';
735
+ denial_reason := 'accepted_attempt_authority_required';
736
+ RETURN NEXT; RETURN;
737
+ END IF;
738
+ RETURN QUERY SELECT * FROM resolve_connection_use_authority_legacy_0256(
739
+ p_account_id, p_workspace_id, p_session_id, p_snapshot
740
+ );
741
+ END
742
+ $body$
743
+ $ddl$, data_schema);
744
+
745
+ EXECUTE format($ddl$
746
+ CREATE OR REPLACE FUNCTION %1$I.resolve_accepted_connection_use(
747
+ p_account_id uuid,
748
+ p_workspace_id uuid,
749
+ p_session_id uuid,
750
+ p_turn_id uuid,
751
+ p_attempt_id uuid,
752
+ p_execution_generation integer,
753
+ p_physical_request_id uuid,
754
+ p_use_phase text,
755
+ p_server_id text,
756
+ p_connection_id uuid,
757
+ p_provider_domain text,
758
+ p_connection_kind text,
759
+ p_subject_scope text,
760
+ p_owner_subject_id text DEFAULT NULL
761
+ ) RETURNS TABLE (
762
+ authorization_status text,
763
+ denial_reason text,
764
+ resolved_connection_id uuid,
765
+ connection_generation bigint,
766
+ origin_workspace_id uuid,
767
+ resolved_connection_kind text,
768
+ authority_scope text,
769
+ owner_subject_id text,
770
+ authority_id uuid,
771
+ grant_id uuid
772
+ )
773
+ LANGUAGE plpgsql
774
+ SECURITY DEFINER
775
+ SET search_path = %1$I, pg_catalog, public, pg_temp
776
+ AS $body$
777
+ #variable_conflict use_column
778
+ DECLARE
779
+ prior connection_use_audit_facts%%ROWTYPE;
780
+ connection_row connections%%ROWTYPE;
781
+ snapshot turn_connection_authority_snapshots%%ROWTYPE;
782
+ session_row sessions%%ROWTYPE;
783
+ grant_row organization_user_resource_grants%%ROWTYPE;
784
+ request_digest bytea;
785
+ has_prior boolean := false;
786
+ reason text;
787
+ BEGIN
788
+ IF p_account_id IS DISTINCT FROM nullif(current_setting('opengeni.account_id', true), '')::uuid
789
+ OR p_workspace_id IS DISTINCT FROM nullif(current_setting('opengeni.workspace_id', true), '')::uuid
790
+ OR p_execution_generation <= 0
791
+ OR p_use_phase NOT IN ('credential_resolution', 'provider_request')
792
+ OR nullif(btrim(p_server_id), '') IS NULL
793
+ OR octet_length(p_server_id) > 256
794
+ THEN
795
+ RAISE EXCEPTION 'connection use scope mismatch' USING ERRCODE = '42501';
796
+ END IF;
797
+ request_digest := digest(convert_to(jsonb_build_object(
798
+ 'accountId', p_account_id, 'workspaceId', p_workspace_id,
799
+ 'sessionId', p_session_id, 'turnId', p_turn_id, 'attemptId', p_attempt_id,
800
+ 'executionGeneration', p_execution_generation, 'usePhase', p_use_phase,
801
+ 'serverId', p_server_id,
802
+ 'connectionId', p_connection_id, 'providerDomain', lower(p_provider_domain),
803
+ 'connectionKind', p_connection_kind, 'subjectScope', p_subject_scope,
804
+ 'ownerSubjectId', p_owner_subject_id
805
+ )::text, 'UTF8'), 'sha256');
806
+
807
+ -- Match the canonical lifecycle lock prefix before inspecting mutable
808
+ -- attempt authority: control -> workspace -> session -> turn -> attempt.
809
+ PERFORM 1 FROM workspace_inference_controls control_row
810
+ WHERE control_row.account_id = p_account_id
811
+ AND control_row.workspace_id = p_workspace_id
812
+ FOR SHARE;
813
+ IF NOT FOUND THEN reason := 'session_identity_changed'; END IF;
814
+ IF reason IS NULL THEN
815
+ PERFORM 1 FROM workspaces workspace_value
816
+ WHERE workspace_value.account_id = p_account_id AND workspace_value.id = p_workspace_id
817
+ FOR KEY SHARE;
818
+ IF NOT FOUND THEN reason := 'session_identity_changed'; END IF;
819
+ END IF;
820
+ IF reason IS NULL THEN
821
+ SELECT session_value.* INTO session_row FROM sessions session_value
822
+ WHERE session_value.id = p_session_id
823
+ AND session_value.account_id = p_account_id
824
+ AND session_value.workspace_id = p_workspace_id
825
+ FOR NO KEY UPDATE;
826
+ IF NOT FOUND OR session_row.active_turn_id IS DISTINCT FROM p_turn_id
827
+ OR session_row.status = 'cancelled'
828
+ THEN reason := 'session_identity_changed'; END IF;
829
+ END IF;
830
+ IF reason IS NULL THEN
831
+ PERFORM 1 FROM session_turns turn_value
832
+ WHERE turn_value.id = p_turn_id
833
+ AND turn_value.account_id = p_account_id
834
+ AND turn_value.workspace_id = p_workspace_id
835
+ AND turn_value.session_id = p_session_id
836
+ AND turn_value.active_attempt_id = p_attempt_id
837
+ AND turn_value.execution_generation = p_execution_generation
838
+ AND turn_value.status = 'running'
839
+ FOR UPDATE;
840
+ IF NOT FOUND THEN reason := 'session_identity_changed'; END IF;
841
+ END IF;
842
+ IF reason IS NULL THEN
843
+ PERFORM 1 FROM session_turn_attempts attempt
844
+ WHERE attempt.id = p_attempt_id
845
+ AND attempt.account_id = p_account_id
846
+ AND attempt.workspace_id = p_workspace_id
847
+ AND attempt.session_id = p_session_id
848
+ AND attempt.turn_id = p_turn_id
849
+ AND attempt.execution_generation = p_execution_generation
850
+ AND attempt.state IN ('claimed', 'running')
851
+ AND attempt.closed_at IS NULL AND attempt.quiesced_at IS NULL
852
+ AND attempt.authority_visibility = session_row.visibility
853
+ AND attempt.authority_epoch = session_row.authority_epoch
854
+ AND attempt.authority_owner_organization_membership_id
855
+ IS NOT DISTINCT FROM session_row.owner_organization_membership_id
856
+ FOR UPDATE;
857
+ IF NOT FOUND OR EXISTS (
858
+ SELECT 1 FROM session_attempt_interruptions interruption
859
+ WHERE interruption.account_id = p_account_id
860
+ AND interruption.workspace_id = p_workspace_id
861
+ AND interruption.session_id = p_session_id
862
+ AND interruption.attempt_id = p_attempt_id
863
+ AND interruption.state IN ('pending', 'delivered', 'acknowledged')
864
+ ) THEN reason := 'session_identity_changed'; END IF;
865
+ END IF;
866
+
867
+ -- Serialize and verify one caller-preallocated physical request identity
868
+ -- after the canonical lifecycle locks even when those live fences now deny.
869
+ -- A stale attempt cannot use that denial to hide conflicting UUID reuse.
870
+ PERFORM pg_advisory_xact_lock(hashtextextended(p_physical_request_id::text, 0));
871
+ SELECT EXISTS (
872
+ SELECT 1 FROM connection_use_audit_facts audit
873
+ WHERE audit.physical_request_id = p_physical_request_id
874
+ ) INTO has_prior;
875
+ IF has_prior THEN
876
+ SELECT audit.* INTO STRICT prior FROM connection_use_audit_facts audit
877
+ WHERE audit.physical_request_id = p_physical_request_id;
878
+ END IF;
879
+ IF has_prior AND prior.request_digest IS DISTINCT FROM request_digest THEN
880
+ RAISE EXCEPTION 'physical connection request id was reused for different work'
881
+ USING ERRCODE = '23505';
882
+ END IF;
883
+ IF reason IS NULL AND has_prior AND prior.outcome = 'denied' THEN
884
+ authorization_status := prior.outcome;
885
+ denial_reason := prior.denial_reason;
886
+ RETURN NEXT; RETURN;
887
+ END IF;
888
+
889
+ IF reason IS NULL THEN
890
+ SELECT authority_snapshot.* INTO snapshot
891
+ FROM turn_connection_authority_snapshots authority_snapshot
892
+ WHERE authority_snapshot.turn_id = p_turn_id
893
+ AND authority_snapshot.server_id = p_server_id
894
+ AND authority_snapshot.account_id = p_account_id
895
+ AND authority_snapshot.workspace_id = p_workspace_id
896
+ AND authority_snapshot.session_id = p_session_id;
897
+
898
+ IF FOUND THEN
899
+ SELECT connection_value.* INTO connection_row FROM connections connection_value
900
+ WHERE connection_value.id = snapshot.connection_id
901
+ AND connection_value.account_id = p_account_id;
902
+ IF snapshot.snapshot_digest IS DISTINCT FROM digest(
903
+ convert_to(snapshot.canonical_snapshot::text, 'UTF8'), 'sha256'
904
+ ) THEN reason := 'accepted_snapshot_digest_changed';
905
+ ELSIF NOT FOUND THEN reason := 'connection_missing';
906
+ ELSIF snapshot.authority_scope IS DISTINCT FROM 'user'
907
+ OR p_subject_scope IS DISTINCT FROM 'subject'
908
+ OR p_connection_id IS DISTINCT FROM snapshot.connection_id
909
+ OR lower(p_provider_domain) IS DISTINCT FROM snapshot.provider_domain
910
+ OR (p_connection_kind IS NOT NULL AND p_connection_kind IS DISTINCT FROM snapshot.connection_kind)
911
+ THEN reason := 'connection_identity_changed';
912
+ ELSIF connection_row.status <> 'active' THEN reason := 'connection_status_inactive';
913
+ ELSIF connection_row.authority_scope IS DISTINCT FROM 'user'
914
+ OR connection_row.subject_id IS DISTINCT FROM snapshot.owner_subject_id
915
+ OR connection_row.owner_organization_membership_id
916
+ IS DISTINCT FROM snapshot.owner_organization_membership_id
917
+ OR connection_row.origin_workspace_id IS DISTINCT FROM snapshot.origin_workspace_id
918
+ THEN reason := 'connection_owner_changed';
919
+ ELSIF connection_row.authority_generation IS DISTINCT FROM snapshot.connection_generation
920
+ THEN reason := 'connection_generation_changed';
921
+ ELSIF session_row.visibility IS DISTINCT FROM snapshot.session_visibility
922
+ OR session_row.authority_epoch IS DISTINCT FROM snapshot.session_authority_epoch
923
+ THEN reason := 'session_authority_epoch_changed';
924
+ ELSIF NOT EXISTS (
925
+ SELECT 1 FROM organization_memberships membership
926
+ WHERE membership.id = snapshot.owner_organization_membership_id
927
+ AND membership.account_id = p_account_id
928
+ AND membership.subject_id = snapshot.owner_subject_id
929
+ AND membership.status = 'active' AND membership.revoked_at IS NULL
930
+ AND membership.authorization_revision = snapshot.membership_authorization_revision
931
+ AND (membership.personal_workspace_id = p_workspace_id OR EXISTS (
932
+ SELECT 1 FROM workspace_memberships workspace_membership
933
+ WHERE workspace_membership.account_id = p_account_id
934
+ AND workspace_membership.workspace_id = p_workspace_id
935
+ AND workspace_membership.subject_id = snapshot.owner_subject_id
936
+ ))
937
+ ) THEN reason := 'owner_membership_inactive';
938
+ ELSIF NOT EXISTS (
939
+ SELECT 1 FROM organization_user_resource_authorities authority
940
+ WHERE authority.id = snapshot.authority_id
941
+ AND authority.account_id = p_account_id
942
+ AND authority.organization_membership_id = snapshot.owner_organization_membership_id
943
+ AND authority.resource_kind = 'connection'
944
+ AND authority.resource_id = snapshot.connection_id
945
+ AND authority.origin_workspace_id = snapshot.origin_workspace_id
946
+ AND authority.generation = snapshot.authority_generation
947
+ AND authority.status = 'active' AND authority.revoked_at IS NULL
948
+ ) THEN reason := 'authority_status_inactive';
949
+ ELSE
950
+ SELECT grant_value.* INTO grant_row
951
+ FROM organization_user_resource_grants grant_value
952
+ WHERE grant_value.id = snapshot.grant_id
953
+ AND grant_value.account_id = p_account_id
954
+ FOR UPDATE;
955
+ IF NOT FOUND THEN reason := 'grant_missing';
956
+ ELSIF grant_row.authority_id IS DISTINCT FROM snapshot.authority_id
957
+ OR grant_row.owner_organization_membership_id
958
+ IS DISTINCT FROM snapshot.owner_organization_membership_id
959
+ OR grant_row.workspace_id IS DISTINCT FROM p_workspace_id
960
+ OR grant_row.action <> 'connection.use'
961
+ OR grant_row.mode IS DISTINCT FROM snapshot.grant_mode
962
+ OR grant_row.context IS DISTINCT FROM snapshot.grant_context
963
+ OR grant_row.session_id IS DISTINCT FROM snapshot.grant_session_id
964
+ OR grant_row.authority_epoch IS DISTINCT FROM snapshot.grant_authority_epoch
965
+ THEN reason := 'grant_identity_changed';
966
+ ELSIF grant_row.generation IS DISTINCT FROM snapshot.grant_generation
967
+ THEN reason := 'grant_generation_changed';
968
+ ELSIF grant_row.expires_at IS NOT NULL AND grant_row.expires_at <= clock_timestamp()
969
+ THEN reason := 'grant_expired';
970
+ ELSIF grant_row.mode = 'once' THEN
971
+ IF grant_row.status <> 'consumed' OR NOT EXISTS (
972
+ SELECT 1 FROM connection_use_once_consumption_receipts receipt
973
+ WHERE receipt.grant_id = grant_row.id
974
+ AND receipt.accepted_work_kind = 'turn'
975
+ AND receipt.accepted_work_id = p_turn_id
976
+ AND receipt.authority_id = snapshot.authority_id
977
+ AND receipt.authority_generation = snapshot.authority_generation
978
+ AND receipt.grant_generation = snapshot.grant_generation
979
+ ) THEN reason := 'grant_already_consumed';
980
+ END IF;
981
+ ELSIF grant_row.status <> 'active' THEN reason := 'grant_status_inactive';
982
+ END IF;
983
+ END IF;
984
+ resolved_connection_id := snapshot.connection_id;
985
+ connection_generation := snapshot.connection_generation;
986
+ origin_workspace_id := snapshot.origin_workspace_id;
987
+ resolved_connection_kind := snapshot.connection_kind;
988
+ authority_scope := snapshot.authority_scope;
989
+ owner_subject_id := snapshot.owner_subject_id;
990
+ authority_id := snapshot.authority_id;
991
+ grant_id := snapshot.grant_id;
992
+ ELSE
993
+ -- A true pre-tenancy legacy_user row has no common authority/grant to
994
+ -- snapshot. It may use only the exact connection frozen on the turn and
995
+ -- still receives the exact live-attempt, destination, generation, and
996
+ -- per-request audit fences above. Common `user` rows never enter this
997
+ -- compatibility lane: already-accepted pre-cutover work therefore fails
998
+ -- closed instead of falling back to its owner tuple.
999
+ SELECT connection_value.* INTO connection_row FROM connections connection_value
1000
+ WHERE connection_value.id = p_connection_id
1001
+ AND connection_value.account_id = p_account_id;
1002
+ IF NOT FOUND THEN reason := 'connection_missing';
1003
+ ELSIF connection_row.authority_scope IS DISTINCT FROM 'legacy_user'
1004
+ OR p_subject_scope IS DISTINCT FROM 'subject'
1005
+ OR p_owner_subject_id IS NULL
1006
+ OR connection_row.subject_id IS DISTINCT FROM p_owner_subject_id
1007
+ OR connection_row.workspace_id IS DISTINCT FROM p_workspace_id
1008
+ OR connection_row.origin_workspace_id IS DISTINCT FROM p_workspace_id
1009
+ OR lower(connection_row.provider_domain) IS DISTINCT FROM lower(p_provider_domain)
1010
+ OR (p_connection_kind IS NOT NULL
1011
+ AND connection_row.kind IS DISTINCT FROM p_connection_kind)
1012
+ THEN reason := 'connection_identity_changed';
1013
+ ELSIF connection_row.status <> 'active' THEN reason := 'connection_status_inactive';
1014
+ ELSE
1015
+ resolved_connection_id := connection_row.id;
1016
+ connection_generation := connection_row.authority_generation;
1017
+ origin_workspace_id := connection_row.origin_workspace_id;
1018
+ resolved_connection_kind := connection_row.kind;
1019
+ authority_scope := 'legacy_user';
1020
+ owner_subject_id := connection_row.subject_id;
1021
+ END IF;
1022
+ END IF;
1023
+ END IF;
1024
+
1025
+ authorization_status := CASE WHEN reason IS NULL THEN 'authorized' ELSE 'denied' END;
1026
+ denial_reason := reason;
1027
+ IF NOT has_prior THEN
1028
+ INSERT INTO connection_use_audit_facts (
1029
+ physical_request_id, use_phase, request_digest, account_id, workspace_id, session_id, turn_id,
1030
+ attempt_id, execution_generation, server_id, connection_id,
1031
+ connection_generation, authority_scope, owner_subject_id, authority_id,
1032
+ grant_id, outcome, denial_reason
1033
+ ) VALUES (
1034
+ p_physical_request_id, p_use_phase, request_digest,
1035
+ p_account_id, p_workspace_id, p_session_id, p_turn_id,
1036
+ p_attempt_id, p_execution_generation, p_server_id, resolved_connection_id,
1037
+ connection_generation, authority_scope, owner_subject_id, authority_id,
1038
+ grant_id, authorization_status, denial_reason
1039
+ ) ON CONFLICT (physical_request_id) DO NOTHING;
1040
+ END IF;
1041
+ RETURN NEXT;
1042
+ END
1043
+ $body$
1044
+ $ddl$, data_schema);
1045
+ END
1046
+ $connection_authority_runtime$;
1047
+
1048
+ REVOKE ALL ON FUNCTION opengeni_private.capture_accepted_turn_connection_authorities() FROM PUBLIC;
1049
+ REVOKE ALL ON FUNCTION resolve_connection_use_authority_legacy_0256(
1050
+ uuid, uuid, uuid, jsonb
1051
+ ) FROM PUBLIC;
1052
+ REVOKE ALL ON FUNCTION resolve_connection_use_authority(uuid, uuid, uuid, jsonb) FROM PUBLIC;
1053
+ REVOKE ALL ON FUNCTION resolve_personal_connection_authority_selection(
1054
+ uuid, uuid, text, uuid, jsonb
1055
+ ) FROM PUBLIC;
1056
+ REVOKE ALL ON FUNCTION resolve_accepted_connection_use(
1057
+ uuid, uuid, uuid, uuid, uuid, integer, uuid, text, text, uuid, text, text, text, text
1058
+ ) FROM PUBLIC;
1059
+
1060
+ DO $grants$
1061
+ BEGIN
1062
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
1063
+ REVOKE ALL ON FUNCTION resolve_connection_use_authority_legacy_0256(
1064
+ uuid, uuid, uuid, jsonb
1065
+ ) FROM opengeni_app;
1066
+ GRANT EXECUTE ON FUNCTION resolve_connection_use_authority(
1067
+ uuid, uuid, uuid, jsonb
1068
+ ) TO opengeni_app;
1069
+ GRANT EXECUTE ON FUNCTION resolve_personal_connection_authority_selection(
1070
+ uuid, uuid, text, uuid, jsonb
1071
+ ) TO opengeni_app;
1072
+ GRANT EXECUTE ON FUNCTION resolve_accepted_connection_use(
1073
+ uuid, uuid, uuid, uuid, uuid, integer, uuid, text, text, uuid, text, text, text, text
1074
+ ) TO opengeni_app;
1075
+ REVOKE ALL ON TABLE turn_connection_authority_snapshots FROM opengeni_app;
1076
+ REVOKE ALL ON TABLE connection_use_audit_facts FROM opengeni_app;
1077
+ END IF;
1078
+ END
1079
+ $grants$;
1080
+
1081
+ COMMENT ON TABLE "turn_connection_authority_snapshots" IS
1082
+ 'Canonical credential-free connection authority frozen for one accepted logical turn; runtime access is only through exact-attempt SECURITY DEFINER resolution.';
1083
+ COMMENT ON TABLE "connection_use_audit_facts" IS
1084
+ 'Idempotent metadata-only facts for physical connection requests; contains no token, header, provider payload, response, quota, or usage value.';