@opengeni/db 1.0.1 → 3.1.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 (265) 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-LTG3S6ZY.js → chunk-EULAN2NP.js} +2 -2
  7. package/dist/{chunk-CUPM3FGB.js → chunk-NJVZPWXY.js} +3 -3
  8. package/dist/{chunk-4SR4HKKA.js → chunk-NMUBV7Z5.js} +19 -11
  9. package/dist/chunk-NMUBV7Z5.js.map +1 -0
  10. package/dist/chunk-OF5OBB76.js +4457 -0
  11. package/dist/chunk-OF5OBB76.js.map +1 -0
  12. package/dist/{chunk-6ZYKCRFO.js → chunk-PR2WMLOQ.js} +7580 -4417
  13. package/dist/chunk-PR2WMLOQ.js.map +1 -0
  14. package/dist/{chunk-ZWREYEWA.js → chunk-SCPUJS53.js} +41 -4
  15. package/dist/chunk-SCPUJS53.js.map +1 -0
  16. package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
  17. package/dist/chunk-SIBYBA2D.js.map +1 -0
  18. package/dist/chunk-TIKF3KTI.js +383 -0
  19. package/dist/chunk-TIKF3KTI.js.map +1 -0
  20. package/dist/chunk-ZZ5LJEFX.js +516 -0
  21. package/dist/chunk-ZZ5LJEFX.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 +41 -0
  33. package/dist/connection-token-resolver.d.ts +30 -1
  34. package/dist/database.d.ts +19 -0
  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 +1595 -81
  43. package/dist/index.js +21571 -8126
  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 +22 -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 +41 -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 +153 -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 +1 -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/package.json +10 -6
  189. package/src/attached-browser-devices.ts +233 -5
  190. package/src/automations.ts +911 -0
  191. package/src/browser-sessions.ts +10 -0
  192. package/src/capability-integrations.ts +175 -1
  193. package/src/child-lifecycle-notices.ts +241 -0
  194. package/src/codemode-operations.ts +1 -35
  195. package/src/company-brain-context-selection-schema.ts +100 -0
  196. package/src/company-brain-context-selection.ts +359 -0
  197. package/src/company-brain-governed-writes.ts +1170 -0
  198. package/src/company-brain.ts +309 -0
  199. package/src/company-profile-agent-admin.ts +225 -0
  200. package/src/company-profile-schema.ts +121 -1
  201. package/src/company-profile.ts +21 -0
  202. package/src/computer-sessions.ts +10 -1
  203. package/src/connection-authority.ts +290 -0
  204. package/src/connection-scopes.ts +20 -0
  205. package/src/connection-tenancy-backfill.ts +81 -0
  206. package/src/connection-token-resolver.ts +263 -31
  207. package/src/database.ts +75 -2
  208. package/src/editable-artifacts-schema.ts +10 -5
  209. package/src/editable-artifacts.ts +21 -11
  210. package/src/governed-learning-activation-schema.ts +121 -0
  211. package/src/governed-learning-activation.ts +588 -0
  212. package/src/governed-learning-evaluator-schema.ts +72 -0
  213. package/src/governed-learning-evaluator.ts +217 -0
  214. package/src/index.ts +11651 -1548
  215. package/src/insights.ts +107 -0
  216. package/src/integration-bindings.ts +88 -6
  217. package/src/integration-facets.ts +287 -84
  218. package/src/interaction-placement-loss.ts +234 -0
  219. package/src/managed-human-provisioning.ts +116 -0
  220. package/src/memory-domain.ts +27 -32
  221. package/src/migrate.ts +77 -1
  222. package/src/new-session-drafts.ts +229 -35
  223. package/src/organization-membership-backfill.ts +735 -0
  224. package/src/organization-membership-lifecycle.ts +985 -0
  225. package/src/pr-review.ts +890 -0
  226. package/src/preference-registry.ts +31 -1
  227. package/src/provision-roles.ts +877 -6
  228. package/src/runtime-posture-cli.ts +2 -0
  229. package/src/runtime-posture.ts +1291 -76
  230. package/src/schema.ts +3303 -428
  231. package/src/scoped-knowledge-schema.ts +16 -8
  232. package/src/scoped-knowledge.ts +1 -1
  233. package/src/session-background-commands.ts +759 -0
  234. package/src/session-control.ts +640 -74
  235. package/src/session-goal-pacing.ts +126 -0
  236. package/src/session-queue-commands.ts +437 -84
  237. package/src/session-realtime-context.ts +3 -3
  238. package/src/session-realtime-ledger.ts +22 -11
  239. package/src/session-realtime-mirror.ts +1 -1
  240. package/src/session-tenancy.ts +403 -49
  241. package/src/session-tool-call-settlement.ts +38 -29
  242. package/src/slack-routing-personal-workspace.ts +67 -0
  243. package/src/slack-routing.ts +469 -0
  244. package/src/slack-task-policy-schema.ts +5 -0
  245. package/src/slack-task-policy.ts +18 -0
  246. package/src/slack-user-link-access.ts +17 -1
  247. package/src/task-notes-schema.ts +74 -0
  248. package/src/task-notes.ts +124 -2
  249. package/src/turn-initiator.ts +8 -0
  250. package/src/user-resource-authority.ts +377 -0
  251. package/src/workspace-authority.ts +160 -0
  252. package/src/workspace-instruction-policies-schema.ts +53 -1
  253. package/src/workspace-instruction-policies.ts +528 -77
  254. package/src/workspace-learning-policy.ts +21 -5
  255. package/src/workspace-membership-permissions.ts +9 -0
  256. package/dist/chunk-4SR4HKKA.js.map +0 -1
  257. package/dist/chunk-6ZYKCRFO.js.map +0 -1
  258. package/dist/chunk-NXAFLCQX.js +0 -2468
  259. package/dist/chunk-NXAFLCQX.js.map +0 -1
  260. package/dist/chunk-SK7YYHBI.js.map +0 -1
  261. package/dist/chunk-WD5ROSLL.js +0 -117
  262. package/dist/chunk-WD5ROSLL.js.map +0 -1
  263. package/dist/chunk-ZWREYEWA.js.map +0 -1
  264. /package/dist/{chunk-LTG3S6ZY.js.map → chunk-EULAN2NP.js.map} +0 -0
  265. /package/dist/{chunk-CUPM3FGB.js.map → chunk-NJVZPWXY.js.map} +0 -0
@@ -0,0 +1,590 @@
1
+ -- deployment-mode: rolling
2
+ -- Durable session-owned background commands. The command row is the
3
+ -- provider-neutral lifecycle/control authority; managed commands link one-to-one
4
+ -- to the exact retained process that still owns immutable launch authority.
5
+ SET LOCAL lock_timeout = '5s';
6
+ SET LOCAL statement_timeout = '10min';
7
+
8
+ CREATE TABLE session_background_commands (
9
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
10
+ account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
11
+ workspace_id uuid NOT NULL,
12
+ session_id uuid NOT NULL,
13
+ provider text NOT NULL,
14
+ state text NOT NULL DEFAULT 'running',
15
+ retained_process_id uuid,
16
+ control_workspace_id uuid,
17
+ enrollment_id uuid,
18
+ connection_instance_id text,
19
+ op_id text,
20
+ command_preview text NOT NULL DEFAULT '',
21
+ cancel_requested_at timestamptz,
22
+ cancel_requested_by text,
23
+ exit_code integer,
24
+ settlement_reason text,
25
+ started_at timestamptz NOT NULL DEFAULT clock_timestamp(),
26
+ settled_at timestamptz,
27
+ reconcile_after timestamptz NOT NULL DEFAULT clock_timestamp(),
28
+ reconcile_claim_id uuid,
29
+ reconcile_claimed_at timestamptz,
30
+ reconcile_attempts integer NOT NULL DEFAULT 0,
31
+ last_reconcile_outcome text,
32
+ reconcile_proof_outcome text,
33
+ reconcile_proof_exit_code integer,
34
+ reconcile_proof_reason text,
35
+ reconcile_proof_observed_at timestamptz,
36
+ updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
37
+ CONSTRAINT session_background_commands_workspace_fk
38
+ FOREIGN KEY (workspace_id, account_id)
39
+ REFERENCES workspaces(id, account_id) ON DELETE CASCADE,
40
+ CONSTRAINT session_background_commands_session_fk
41
+ FOREIGN KEY (workspace_id, session_id)
42
+ REFERENCES sessions(workspace_id, id) ON DELETE CASCADE,
43
+ CONSTRAINT session_background_commands_control_workspace_fk
44
+ FOREIGN KEY (control_workspace_id, account_id)
45
+ REFERENCES workspaces(id, account_id) ON DELETE RESTRICT,
46
+ CONSTRAINT session_background_commands_process_fk
47
+ FOREIGN KEY (retained_process_id)
48
+ REFERENCES sandbox_retained_processes(id) ON DELETE RESTRICT,
49
+ CONSTRAINT session_background_commands_provider_check CHECK (
50
+ provider IN ('managed', 'connected_machine')
51
+ ),
52
+ CONSTRAINT session_background_commands_state_check CHECK (
53
+ state IN ('running', 'stopping', 'exited', 'lost')
54
+ ),
55
+ CONSTRAINT session_background_commands_provider_identity_check CHECK (
56
+ (
57
+ provider = 'managed'
58
+ AND retained_process_id IS NOT NULL
59
+ AND control_workspace_id IS NULL
60
+ AND enrollment_id IS NULL
61
+ AND connection_instance_id IS NULL
62
+ AND op_id IS NULL
63
+ ) OR (
64
+ provider = 'connected_machine'
65
+ AND retained_process_id IS NULL
66
+ AND control_workspace_id IS NOT NULL
67
+ AND enrollment_id IS NOT NULL
68
+ AND connection_instance_id IS NOT NULL
69
+ AND octet_length(connection_instance_id) BETWEEN 1 AND 128
70
+ AND op_id IS NOT NULL
71
+ AND octet_length(op_id) BETWEEN 1 AND 256
72
+ )
73
+ ),
74
+ CONSTRAINT session_background_commands_lifecycle_check CHECK (
75
+ (
76
+ state = 'running'
77
+ AND cancel_requested_at IS NULL
78
+ AND cancel_requested_by IS NULL
79
+ AND exit_code IS NULL
80
+ AND settlement_reason IS NULL
81
+ AND settled_at IS NULL
82
+ ) OR (
83
+ state = 'stopping'
84
+ AND cancel_requested_at IS NOT NULL
85
+ AND cancel_requested_by IS NOT NULL
86
+ AND octet_length(btrim(cancel_requested_by)) BETWEEN 1 AND 1024
87
+ AND exit_code IS NULL
88
+ AND settlement_reason IS NULL
89
+ AND settled_at IS NULL
90
+ ) OR (
91
+ state = 'exited'
92
+ AND settled_at IS NOT NULL
93
+ AND octet_length(btrim(settlement_reason)) BETWEEN 1 AND 512
94
+ ) OR (
95
+ state = 'lost'
96
+ AND exit_code IS NULL
97
+ AND settled_at IS NOT NULL
98
+ AND octet_length(btrim(settlement_reason)) BETWEEN 1 AND 512
99
+ )
100
+ ),
101
+ CONSTRAINT session_background_commands_preview_check CHECK (
102
+ octet_length(command_preview) <= 2048
103
+ ),
104
+ CONSTRAINT session_background_commands_reconcile_check CHECK (
105
+ reconcile_attempts >= 0
106
+ AND (
107
+ (reconcile_claim_id IS NULL AND reconcile_claimed_at IS NULL)
108
+ OR (reconcile_claim_id IS NOT NULL AND reconcile_claimed_at IS NOT NULL)
109
+ )
110
+ AND (
111
+ last_reconcile_outcome IS NULL
112
+ OR octet_length(last_reconcile_outcome) BETWEEN 1 AND 64
113
+ )
114
+ AND (
115
+ (
116
+ reconcile_proof_outcome IS NULL
117
+ AND reconcile_proof_exit_code IS NULL
118
+ AND reconcile_proof_reason IS NULL
119
+ AND reconcile_proof_observed_at IS NULL
120
+ ) OR (
121
+ reconcile_proof_outcome = 'exited'
122
+ AND reconcile_proof_exit_code IS NOT NULL
123
+ AND octet_length(btrim(reconcile_proof_reason)) BETWEEN 1 AND 512
124
+ AND reconcile_proof_observed_at IS NOT NULL
125
+ ) OR (
126
+ reconcile_proof_outcome = 'lost'
127
+ AND reconcile_proof_exit_code IS NULL
128
+ AND octet_length(btrim(reconcile_proof_reason)) BETWEEN 1 AND 512
129
+ AND reconcile_proof_observed_at IS NOT NULL
130
+ )
131
+ )
132
+ )
133
+ );
134
+
135
+ CREATE UNIQUE INDEX session_background_commands_process_uq
136
+ ON session_background_commands (retained_process_id)
137
+ WHERE retained_process_id IS NOT NULL;
138
+ CREATE UNIQUE INDEX session_background_commands_connected_op_uq
139
+ ON session_background_commands (
140
+ control_workspace_id, enrollment_id, connection_instance_id, op_id
141
+ ) WHERE provider = 'connected_machine';
142
+ CREATE INDEX session_background_commands_active_session_idx
143
+ ON session_background_commands (workspace_id, session_id, state, started_at, id)
144
+ WHERE state IN ('running', 'stopping');
145
+ CREATE INDEX session_background_commands_stopping_idx
146
+ ON session_background_commands (reconcile_after, cancel_requested_at, id)
147
+ WHERE state IN ('running', 'stopping');
148
+
149
+ ALTER TABLE session_background_commands ENABLE ROW LEVEL SECURITY;
150
+ ALTER TABLE session_background_commands FORCE ROW LEVEL SECURITY;
151
+ CREATE POLICY workspace_isolation ON session_background_commands
152
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
153
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
154
+ CREATE POLICY session_visibility_isolation ON session_background_commands AS RESTRICTIVE
155
+ USING (session_reference_visible(account_id, workspace_id, session_id))
156
+ WITH CHECK (session_reference_visible(account_id, workspace_id, session_id));
157
+
158
+ DO $grants$
159
+ DECLARE data_schema text := current_schema();
160
+ BEGIN
161
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
162
+ EXECUTE format(
163
+ 'GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE %I.session_background_commands TO opengeni_app',
164
+ data_schema
165
+ );
166
+ END IF;
167
+ END
168
+ $grants$;
169
+
170
+ COMMENT ON TABLE session_background_commands IS
171
+ 'Provider-neutral durable lifecycle for commands explicitly transferred from a turn to its session.';
172
+ COMMENT ON COLUMN session_background_commands.retained_process_id IS
173
+ 'Managed provider link. The retained process preserves immutable launch authority and exact provider identity.';
174
+ COMMENT ON COLUMN session_background_commands.connection_instance_id IS
175
+ 'Connected Machine daemon instance admitted at launch; switching the session machine never rewrites it.';
176
+ COMMENT ON COLUMN session_background_commands.control_workspace_id IS
177
+ 'Connected Machine origin workspace used in the exact physical NATS subject; never rewritten from the session workspace.';
178
+
179
+ -- Workspace deletion takes the application-level background-command prefix
180
+ -- before its parent rows. These SECURITY DEFINER seams can therefore inspect
181
+ -- cross-workspace origin references without weakening ordinary workspace RLS:
182
+ -- active authority is checked before any other deletion work, while terminal
183
+ -- history is pruned only at the final successful cascade boundary.
184
+ DO $workspace_delete_function$
185
+ DECLARE data_schema text := current_schema();
186
+ BEGIN
187
+ EXECUTE format($create$
188
+ CREATE FUNCTION opengeni_private.prepare_workspace_background_command_deletion(
189
+ p_account_id uuid,
190
+ p_workspace_id uuid
191
+ )
192
+ RETURNS integer
193
+ LANGUAGE plpgsql
194
+ SECURITY DEFINER
195
+ SET search_path = pg_catalog
196
+ AS $function$
197
+ DECLARE
198
+ active_count integer := 0;
199
+ BEGIN
200
+ IF pg_catalog.current_setting('opengeni.account_id', true)
201
+ IS DISTINCT FROM p_account_id::text
202
+ OR pg_catalog.current_setting('opengeni.workspace_id', true)
203
+ IS DISTINCT FROM p_workspace_id::text THEN
204
+ RAISE EXCEPTION 'workspace background-command deletion scope mismatch'
205
+ USING ERRCODE = '42501';
206
+ END IF;
207
+
208
+ BEGIN
209
+ PERFORM command.id
210
+ FROM %1$I.session_background_commands command
211
+ WHERE command.account_id = p_account_id
212
+ AND command.state IN ('running', 'stopping')
213
+ AND (
214
+ command.workspace_id = p_workspace_id
215
+ OR command.control_workspace_id = p_workspace_id
216
+ )
217
+ ORDER BY command.id
218
+ FOR UPDATE NOWAIT;
219
+ GET DIAGNOSTICS active_count = ROW_COUNT;
220
+ EXCEPTION WHEN lock_not_available THEN
221
+ -- A reconciler currently owns lifecycle proof for a matching command;
222
+ -- deletion remains blocked rather than waiting or losing its locator.
223
+ RETURN 1;
224
+ END;
225
+
226
+ RETURN active_count;
227
+ END
228
+ $function$;
229
+ $create$, data_schema);
230
+ END
231
+ $workspace_delete_function$;
232
+
233
+ DO $workspace_prune_function$
234
+ DECLARE data_schema text := current_schema();
235
+ BEGIN
236
+ EXECUTE format($create$
237
+ CREATE FUNCTION opengeni_private.prune_settled_cross_workspace_background_commands(
238
+ p_account_id uuid,
239
+ p_workspace_id uuid
240
+ )
241
+ RETURNS integer
242
+ LANGUAGE plpgsql
243
+ SECURITY DEFINER
244
+ SET search_path = pg_catalog
245
+ AS $function$
246
+ DECLARE
247
+ deleted_count integer := 0;
248
+ BEGIN
249
+ IF pg_catalog.current_setting('opengeni.account_id', true)
250
+ IS DISTINCT FROM p_account_id::text
251
+ OR pg_catalog.current_setting('opengeni.workspace_id', true)
252
+ IS DISTINCT FROM p_workspace_id::text THEN
253
+ RAISE EXCEPTION 'workspace background-command deletion scope mismatch'
254
+ USING ERRCODE = '42501';
255
+ END IF;
256
+
257
+ DELETE FROM %1$I.session_background_commands command
258
+ WHERE command.account_id = p_account_id
259
+ AND command.control_workspace_id = p_workspace_id
260
+ AND command.workspace_id <> p_workspace_id
261
+ AND command.state IN ('exited', 'lost');
262
+ GET DIAGNOSTICS deleted_count = ROW_COUNT;
263
+ RETURN deleted_count;
264
+ END
265
+ $function$;
266
+ $create$, data_schema);
267
+ END
268
+ $workspace_prune_function$;
269
+
270
+ REVOKE ALL ON FUNCTION opengeni_private.prepare_workspace_background_command_deletion(
271
+ uuid, uuid
272
+ ) FROM PUBLIC;
273
+ REVOKE ALL ON FUNCTION opengeni_private.prune_settled_cross_workspace_background_commands(
274
+ uuid, uuid
275
+ ) FROM PUBLIC;
276
+
277
+ DO $workspace_delete_grant$
278
+ BEGIN
279
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
280
+ GRANT EXECUTE ON FUNCTION opengeni_private.prepare_workspace_background_command_deletion(
281
+ uuid, uuid
282
+ ) TO opengeni_app;
283
+ GRANT EXECUTE ON FUNCTION opengeni_private.prune_settled_cross_workspace_background_commands(
284
+ uuid, uuid
285
+ ) TO opengeni_app;
286
+ END IF;
287
+ END
288
+ $workspace_delete_grant$;
289
+
290
+ DO $settlement_function$
291
+ DECLARE data_schema text := current_schema();
292
+ BEGIN
293
+ EXECUTE format($create$
294
+ CREATE FUNCTION opengeni_private.settle_background_command_from_retained_process()
295
+ RETURNS trigger
296
+ LANGUAGE plpgsql
297
+ SET search_path = pg_catalog
298
+ AS $function$
299
+ BEGIN
300
+ IF OLD.state = 'active' AND NEW.state IN ('exited', 'lost') THEN
301
+ UPDATE %1$I.session_background_commands command SET
302
+ state = NEW.state,
303
+ exit_code = CASE WHEN NEW.state = 'exited' THEN NEW.exit_code ELSE NULL END,
304
+ settlement_reason = NEW.settlement_reason,
305
+ settled_at = NEW.settled_at,
306
+ reconcile_claim_id = NULL,
307
+ reconcile_claimed_at = NULL,
308
+ last_reconcile_outcome = 'settled_' || NEW.state,
309
+ updated_at = pg_catalog.clock_timestamp()
310
+ WHERE command.retained_process_id = NEW.id
311
+ AND command.state IN ('running', 'stopping');
312
+ END IF;
313
+ RETURN NEW;
314
+ END
315
+ $function$;
316
+ $create$, data_schema);
317
+ END
318
+ $settlement_function$;
319
+
320
+ CREATE TRIGGER settle_background_command_from_retained_process
321
+ AFTER UPDATE OF state, exit_code, settlement_reason, settled_at
322
+ ON sandbox_retained_processes
323
+ FOR EACH ROW
324
+ EXECUTE FUNCTION opengeni_private.settle_background_command_from_retained_process();
325
+
326
+ REVOKE ALL ON FUNCTION opengeni_private.settle_background_command_from_retained_process()
327
+ FROM PUBLIC;
328
+
329
+ -- Background-owned managed processes remain eligible for exact polling even
330
+ -- while their launch attempt is open/closed independently. Stopping state is
331
+ -- projected through owner_state so the reaper can issue one exact cancellation
332
+ -- before observing terminal proof.
333
+ DO $claim_function$
334
+ DECLARE data_schema text := current_schema();
335
+ BEGIN
336
+ EXECUTE format($create$
337
+ CREATE OR REPLACE FUNCTION opengeni_private.claim_terminal_retained_processes(
338
+ p_claim_id uuid,
339
+ p_limit integer,
340
+ p_claim_ttl_ms bigint
341
+ )
342
+ RETURNS TABLE (
343
+ account_id uuid,
344
+ workspace_id uuid,
345
+ session_id uuid,
346
+ process_id uuid,
347
+ claim_id uuid,
348
+ owner_state text,
349
+ owner_attempt_outcome text
350
+ )
351
+ LANGUAGE plpgsql
352
+ SECURITY DEFINER
353
+ SET search_path = pg_catalog
354
+ AS $function$
355
+ BEGIN
356
+ IF p_limit < 1 OR p_limit > 100 THEN
357
+ RAISE EXCEPTION 'retained-process reconciliation limit must be between 1 and 100'
358
+ USING ERRCODE = '22023';
359
+ END IF;
360
+ IF p_claim_ttl_ms < 0 OR p_claim_ttl_ms > 3600000 THEN
361
+ RAISE EXCEPTION 'retained-process reconciliation claim TTL is invalid'
362
+ USING ERRCODE = '22023';
363
+ END IF;
364
+
365
+ PERFORM pg_catalog.set_config('opengeni.sandbox_recovery_protocol_v2', '1', true);
366
+
367
+ RETURN QUERY
368
+ WITH candidate_window AS MATERIALIZED (
369
+ SELECT process.id,
370
+ process.workspace_id,
371
+ process.parent_admission_id,
372
+ process.owner_actor_kind,
373
+ process.owner_turn_id,
374
+ process.owner_attempt_id,
375
+ process.reconcile_after AS due_at,
376
+ process.started_at
377
+ FROM %1$I.sandbox_retained_processes process
378
+ WHERE process.state = 'active'
379
+ AND process.reconcile_after <= pg_catalog.now()
380
+ ORDER BY process.reconcile_after, process.started_at, process.id
381
+ FOR UPDATE OF process SKIP LOCKED
382
+ LIMIT p_limit
383
+ ), classified AS MATERIALIZED (
384
+ SELECT candidate.id,
385
+ candidate.due_at,
386
+ candidate.started_at,
387
+ command.state AS command_state,
388
+ command.id IS NOT NULL OR CASE
389
+ WHEN candidate.owner_actor_kind = 'direct' THEN direct_owner.live IS NULL
390
+ ELSE attempt.state = 'closed'
391
+ END AS eligible,
392
+ CASE
393
+ WHEN command.state = 'stopping' THEN 'background_stopping'
394
+ WHEN command.state = 'running' THEN 'background_running'
395
+ WHEN candidate.owner_actor_kind = 'direct' THEN 'direct'
396
+ ELSE COALESCE(turn_row.status, 'missing')
397
+ END AS owner_state,
398
+ attempt.outcome AS owner_attempt_outcome
399
+ FROM candidate_window candidate
400
+ LEFT JOIN %1$I.session_background_commands command
401
+ ON command.retained_process_id = candidate.id
402
+ AND command.state IN ('running', 'stopping')
403
+ LEFT JOIN LATERAL (
404
+ SELECT true AS live
405
+ FROM %1$I.sandbox_workspace_mutation_admissions admission
406
+ JOIN %1$I.sandbox_lease_holders holder
407
+ ON holder.lease_id = admission.lease_id
408
+ AND holder.account_id = admission.account_id
409
+ AND holder.workspace_id = admission.workspace_id
410
+ AND holder.kind = 'direct'
411
+ AND holder.holder_id = admission.holder_id
412
+ AND holder.subject_id = admission.session_id
413
+ WHERE candidate.owner_actor_kind = 'direct'
414
+ AND admission.id = candidate.parent_admission_id
415
+ AND admission.actor_kind = 'direct'
416
+ LIMIT 1
417
+ ) direct_owner ON true
418
+ LEFT JOIN LATERAL (
419
+ SELECT source_turn.status
420
+ FROM %1$I.session_turns source_turn
421
+ WHERE source_turn.workspace_id = candidate.workspace_id
422
+ AND source_turn.id = candidate.owner_turn_id
423
+ LIMIT 1
424
+ ) turn_row ON true
425
+ LEFT JOIN LATERAL (
426
+ SELECT source_attempt.state, source_attempt.outcome
427
+ FROM %1$I.session_turn_attempts source_attempt
428
+ WHERE source_attempt.workspace_id = candidate.workspace_id
429
+ AND source_attempt.id = candidate.owner_attempt_id
430
+ LIMIT 1
431
+ ) attempt ON true
432
+ ), inspected AS (
433
+ UPDATE %1$I.sandbox_retained_processes process SET
434
+ reconcile_after = CASE
435
+ WHEN classified.eligible THEN pg_catalog.now()
436
+ + pg_catalog.make_interval(secs => p_claim_ttl_ms / 1000.0)
437
+ ELSE pg_catalog.now() + interval '30 seconds'
438
+ END,
439
+ reconcile_claim_id = CASE WHEN classified.eligible THEN p_claim_id ELSE NULL END,
440
+ reconcile_claimed_at = CASE
441
+ WHEN classified.eligible THEN pg_catalog.now() ELSE NULL
442
+ END,
443
+ reconcile_attempts = process.reconcile_attempts
444
+ + CASE WHEN classified.eligible THEN 1 ELSE 0 END,
445
+ last_reconcile_outcome = CASE
446
+ WHEN classified.eligible THEN 'claimed' ELSE 'owner_active'
447
+ END
448
+ FROM classified
449
+ WHERE process.id = classified.id
450
+ AND process.state = 'active'
451
+ RETURNING process.account_id,
452
+ process.workspace_id,
453
+ process.session_id,
454
+ process.id,
455
+ process.reconcile_claim_id,
456
+ classified.due_at,
457
+ classified.started_at,
458
+ classified.eligible,
459
+ classified.owner_state,
460
+ classified.owner_attempt_outcome
461
+ )
462
+ SELECT inspected.account_id,
463
+ inspected.workspace_id,
464
+ inspected.session_id,
465
+ inspected.id,
466
+ inspected.reconcile_claim_id,
467
+ inspected.owner_state,
468
+ inspected.owner_attempt_outcome
469
+ FROM inspected
470
+ WHERE inspected.eligible
471
+ ORDER BY inspected.due_at, inspected.started_at, inspected.id;
472
+
473
+ SET CONSTRAINTS %1$I.sandbox_retained_processes_identity_v2 IMMEDIATE;
474
+ SET CONSTRAINTS %1$I.sandbox_retained_processes_identity_v2 DEFERRED;
475
+ END;
476
+ $function$;
477
+ $create$, data_schema);
478
+ END
479
+ $claim_function$;
480
+
481
+ -- Connected Machine commands are reconciled directly against the immutable
482
+ -- launch locator. Claim expiry recovers a dead reaper only; it is never treated
483
+ -- as provider exit/loss evidence.
484
+ DO $connected_claim_function$
485
+ DECLARE data_schema text := current_schema();
486
+ BEGIN
487
+ EXECUTE format($create$
488
+ CREATE FUNCTION opengeni_private.claim_connected_machine_background_commands(
489
+ p_claim_id uuid,
490
+ p_limit integer,
491
+ p_claim_ttl_ms bigint
492
+ )
493
+ RETURNS TABLE (
494
+ account_id uuid,
495
+ workspace_id uuid,
496
+ session_id uuid,
497
+ command_id uuid,
498
+ claim_id uuid,
499
+ command_state text,
500
+ control_workspace_id uuid,
501
+ enrollment_id uuid,
502
+ connection_instance_id text,
503
+ op_id text,
504
+ reconcile_attempts integer,
505
+ reconcile_proof_outcome text,
506
+ reconcile_proof_exit_code integer,
507
+ reconcile_proof_reason text,
508
+ reconcile_proof_observed_at timestamptz
509
+ )
510
+ LANGUAGE plpgsql
511
+ SECURITY DEFINER
512
+ SET search_path = pg_catalog
513
+ AS $function$
514
+ BEGIN
515
+ IF p_limit < 1 OR p_limit > 100 THEN
516
+ RAISE EXCEPTION 'connected-command reconciliation limit must be between 1 and 100'
517
+ USING ERRCODE = '22023';
518
+ END IF;
519
+ IF p_claim_ttl_ms < 0 OR p_claim_ttl_ms > 3600000 THEN
520
+ RAISE EXCEPTION 'connected-command reconciliation claim TTL is invalid'
521
+ USING ERRCODE = '22023';
522
+ END IF;
523
+
524
+ RETURN QUERY
525
+ WITH candidates AS MATERIALIZED (
526
+ SELECT command.id, command.reconcile_after, command.started_at
527
+ FROM %1$I.session_background_commands command
528
+ WHERE command.provider = 'connected_machine'
529
+ AND command.state IN ('running', 'stopping')
530
+ AND command.reconcile_after <= pg_catalog.now()
531
+ AND (
532
+ command.reconcile_claim_id IS NULL
533
+ OR command.reconcile_claimed_at <= pg_catalog.now()
534
+ - pg_catalog.make_interval(secs => p_claim_ttl_ms / 1000.0)
535
+ )
536
+ ORDER BY command.reconcile_after, command.started_at, command.id
537
+ FOR UPDATE OF command SKIP LOCKED
538
+ LIMIT p_limit
539
+ ), claimed AS (
540
+ UPDATE %1$I.session_background_commands command SET
541
+ reconcile_after = pg_catalog.now()
542
+ + pg_catalog.make_interval(secs => p_claim_ttl_ms / 1000.0),
543
+ reconcile_claim_id = p_claim_id,
544
+ reconcile_claimed_at = pg_catalog.now(),
545
+ reconcile_attempts = command.reconcile_attempts + 1,
546
+ last_reconcile_outcome = 'claimed',
547
+ updated_at = pg_catalog.clock_timestamp()
548
+ FROM candidates
549
+ WHERE command.id = candidates.id
550
+ AND command.provider = 'connected_machine'
551
+ AND command.state IN ('running', 'stopping')
552
+ RETURNING command.*, candidates.reconcile_after AS due_at,
553
+ candidates.started_at AS candidate_started_at
554
+ )
555
+ SELECT claimed.account_id,
556
+ claimed.workspace_id,
557
+ claimed.session_id,
558
+ claimed.id,
559
+ claimed.reconcile_claim_id,
560
+ claimed.state,
561
+ claimed.control_workspace_id,
562
+ claimed.enrollment_id,
563
+ claimed.connection_instance_id,
564
+ claimed.op_id,
565
+ claimed.reconcile_attempts,
566
+ claimed.reconcile_proof_outcome,
567
+ claimed.reconcile_proof_exit_code,
568
+ claimed.reconcile_proof_reason,
569
+ claimed.reconcile_proof_observed_at
570
+ FROM claimed
571
+ ORDER BY claimed.due_at, claimed.candidate_started_at, claimed.id;
572
+ END;
573
+ $function$;
574
+ $create$, data_schema);
575
+ END
576
+ $connected_claim_function$;
577
+
578
+ REVOKE ALL ON FUNCTION opengeni_private.claim_connected_machine_background_commands(
579
+ uuid, integer, bigint
580
+ ) FROM PUBLIC;
581
+
582
+ DO $connected_claim_grant$
583
+ BEGIN
584
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
585
+ GRANT EXECUTE ON FUNCTION opengeni_private.claim_connected_machine_background_commands(
586
+ uuid, integer, bigint
587
+ ) TO opengeni_app;
588
+ END IF;
589
+ END
590
+ $connected_claim_grant$;
@@ -0,0 +1,91 @@
1
+ -- deployment-mode: rolling
2
+ -- Slack delivery eligibility is decided by this SECURITY DEFINER claim before
3
+ -- the pump ever reads a page. A blocked child worker announces itself on its
4
+ -- Slack-originated parent as system.update.pending, and a budget or
5
+ -- continuation-cap goal pause as goal.paused, so both types must arm a claim.
6
+ -- The pump still decides what is worth posting: only a child_requires_action
7
+ -- notice and a limits / max_auto_continuations pause produce a message.
8
+ -- Rolling-safe: an old API image simply drains the extra claim without posting.
9
+
10
+ SET lock_timeout = '5s';
11
+ SET statement_timeout = '10min';
12
+
13
+ DO $migration$
14
+ DECLARE data_schema text := current_schema();
15
+ BEGIN
16
+ EXECUTE format($ddl$
17
+ CREATE OR REPLACE FUNCTION opengeni_private.claim_slack_interaction_delivery(
18
+ p_holder uuid,
19
+ p_lease_ms integer
20
+ )
21
+ RETURNS SETOF %1$I.slack_interactions
22
+ LANGUAGE plpgsql
23
+ SECURITY DEFINER
24
+ SET search_path = pg_catalog
25
+ AS $function$
26
+ BEGIN
27
+ IF p_lease_ms < 1000 OR p_lease_ms > 300000 THEN
28
+ RAISE EXCEPTION 'invalid Slack delivery claim lease';
29
+ END IF;
30
+ RETURN QUERY
31
+ WITH candidate AS (
32
+ SELECT I.id
33
+ FROM %1$I.slack_interactions I
34
+ WHERE I.session_id IS NOT NULL
35
+ AND I.terminal_delivery_state = 'open'
36
+ AND (I.delivery_retry_at IS NULL OR I.delivery_retry_at <= now())
37
+ AND (I.delivery_claim_holder_id IS NULL OR I.delivery_claim_expires_at <= now())
38
+ AND NOT (
39
+ I.visibility = 'private'
40
+ AND I.triggering_provider_event_id LIKE 'shortcut:%%'
41
+ AND I.ack_slack_message_ts IS NULL
42
+ )
43
+ AND EXISTS (
44
+ SELECT 1
45
+ FROM %1$I.session_events E
46
+ WHERE E.workspace_id = I.workspace_id
47
+ AND E.session_id = I.session_id
48
+ AND E.sequence > I.last_delivered_session_event_sequence
49
+ AND E.type IN (
50
+ 'agent.message.completed',
51
+ 'session.humanInput.requested',
52
+ 'turn.completed',
53
+ 'turn.failed',
54
+ 'turn.cancelled',
55
+ 'session.status.changed',
56
+ 'system.update.pending',
57
+ 'goal.paused'
58
+ )
59
+ )
60
+ ORDER BY I.delivery_retry_at NULLS FIRST, I.updated_at, I.id
61
+ FOR UPDATE SKIP LOCKED
62
+ LIMIT 1
63
+ )
64
+ UPDATE %1$I.slack_interactions I
65
+ SET delivery_claim_holder_id = p_holder,
66
+ delivery_claim_expires_at = now() + make_interval(secs => p_lease_ms::double precision / 1000),
67
+ delivery_attempt_count = I.delivery_attempt_count + 1,
68
+ updated_at = now()
69
+ FROM candidate C
70
+ WHERE I.id = C.id
71
+ RETURNING I.*;
72
+ END
73
+ $function$
74
+ $ddl$, data_schema);
75
+ END
76
+ $migration$;
77
+
78
+ REVOKE ALL ON FUNCTION opengeni_private.claim_slack_interaction_delivery(uuid, integer)
79
+ FROM PUBLIC;
80
+
81
+ DO $grants$
82
+ BEGIN
83
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
84
+ GRANT EXECUTE ON FUNCTION opengeni_private.claim_slack_interaction_delivery(uuid, integer)
85
+ TO opengeni_app;
86
+ END IF;
87
+ END
88
+ $grants$;
89
+
90
+ RESET statement_timeout;
91
+ RESET lock_timeout;
@@ -0,0 +1,20 @@
1
+ -- deployment-mode: rolling
2
+ -- Human-readable API-key descriptions let workspace administrators record the
3
+ -- intended caller or automation without exposing the secret token. Existing
4
+ -- keys remain valid and return a NULL description.
5
+ SET LOCAL lock_timeout = '5s';
6
+ SET LOCAL statement_timeout = '10min';
7
+
8
+ ALTER TABLE "api_keys"
9
+ ADD COLUMN "description" text;
10
+
11
+ ALTER TABLE "api_keys"
12
+ ADD CONSTRAINT "api_keys_description_check"
13
+ CHECK ("description" IS NULL OR length("description") BETWEEN 1 AND 500)
14
+ NOT VALID;
15
+
16
+ ALTER TABLE "api_keys"
17
+ VALIDATE CONSTRAINT "api_keys_description_check";
18
+
19
+ RESET statement_timeout;
20
+ RESET lock_timeout;