@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
package/src/insights.ts CHANGED
@@ -82,6 +82,20 @@ export type ModelCallFacetRow = {
82
82
  model: string;
83
83
  };
84
84
 
85
+ export type ModelContextContributionAggregate = {
86
+ estimatedTokens: number;
87
+ utf8Bytes: number;
88
+ coveredCalls: number;
89
+ totalCalls: number;
90
+ sources: Array<{
91
+ source: string;
92
+ items: number;
93
+ utf8Bytes: number;
94
+ estimatedTokens: number;
95
+ calls: number;
96
+ }>;
97
+ };
98
+
85
99
  export type SessionDepthAggregate = {
86
100
  depth: number;
87
101
  sessions: number;
@@ -645,6 +659,99 @@ export async function listRecentModelCalls(
645
659
  });
646
660
  }
647
661
 
662
+ export async function aggregateModelContextContributions(
663
+ db: Database,
664
+ input: InsightsTimeWindow & {
665
+ workspaceId: string;
666
+ provider?: string | null;
667
+ model?: string | null;
668
+ },
669
+ ): Promise<ModelContextContributionAggregate> {
670
+ const context = await rlsContextForWorkspace(db, input.workspaceId);
671
+ return await withRlsContext(db, context, async (scopedDb) => {
672
+ const clauses = [
673
+ eq(schema.modelCallFacts.workspaceId, input.workspaceId),
674
+ gte(schema.modelCallFacts.occurredAt, input.since),
675
+ lt(schema.modelCallFacts.occurredAt, input.until),
676
+ ...(input.provider ? [eq(schema.modelCallFacts.provider, input.provider)] : []),
677
+ ...(input.model ? [eq(schema.modelCallFacts.model, input.model)] : []),
678
+ ];
679
+ type ContributionRow = {
680
+ total_calls: number | string;
681
+ covered_calls: number | string;
682
+ source: string | null;
683
+ items: number | string | null;
684
+ utf8_bytes: number | string | null;
685
+ estimated_tokens: number | string | null;
686
+ calls: number | string | null;
687
+ };
688
+ const rows = (await scopedDb.execute(sql<ContributionRow>`
689
+ with filtered as (
690
+ select
691
+ ${schema.modelCallFacts.id} as id,
692
+ ${schema.modelCallFacts.contextContributions} as context_contributions
693
+ from ${schema.modelCallFacts}
694
+ where ${and(...clauses)}
695
+ ), coverage as (
696
+ select
697
+ count(*)::bigint as total_calls,
698
+ count(context_contributions)::bigint as covered_calls
699
+ from filtered
700
+ ), source_rows as (
701
+ select
702
+ filtered.id,
703
+ contribution->>'source' as source,
704
+ (contribution->>'items')::bigint as items,
705
+ (contribution->>'utf8Bytes')::bigint as utf8_bytes,
706
+ (contribution->>'estimatedTokens')::bigint as estimated_tokens
707
+ from filtered
708
+ cross join lateral jsonb_array_elements(filtered.context_contributions) contribution
709
+ ), source_totals as (
710
+ select
711
+ source,
712
+ sum(items)::bigint as items,
713
+ sum(utf8_bytes)::bigint as utf8_bytes,
714
+ sum(estimated_tokens)::bigint as estimated_tokens,
715
+ count(distinct id)::bigint as calls
716
+ from source_rows
717
+ group by source
718
+ )
719
+ select
720
+ coverage.total_calls,
721
+ coverage.covered_calls,
722
+ source_totals.source,
723
+ source_totals.items,
724
+ source_totals.utf8_bytes,
725
+ source_totals.estimated_tokens,
726
+ source_totals.calls
727
+ from coverage
728
+ left join source_totals on true
729
+ order by source_totals.estimated_tokens desc nulls last, source_totals.source
730
+ `)) as ContributionRow[];
731
+ const first = rows[0];
732
+ const sources = rows.flatMap((row) =>
733
+ row.source
734
+ ? [
735
+ {
736
+ source: row.source,
737
+ items: Number(row.items ?? 0),
738
+ utf8Bytes: Number(row.utf8_bytes ?? 0),
739
+ estimatedTokens: Number(row.estimated_tokens ?? 0),
740
+ calls: Number(row.calls ?? 0),
741
+ },
742
+ ]
743
+ : [],
744
+ );
745
+ return {
746
+ estimatedTokens: sources.reduce((sum, row) => sum + row.estimatedTokens, 0),
747
+ utf8Bytes: sources.reduce((sum, row) => sum + row.utf8Bytes, 0),
748
+ coveredCalls: Number(first?.covered_calls ?? 0),
749
+ totalCalls: Number(first?.total_calls ?? 0),
750
+ sources,
751
+ };
752
+ });
753
+ }
754
+
648
755
  export async function aggregateScheduleFacts(
649
756
  db: Database,
650
757
  input: InsightsTimeWindow & {
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
 
3
3
  import { stableJson } from "@opengeni/contracts";
4
- import { and, asc, eq, or, sql } from "drizzle-orm";
4
+ import { and, asc, eq, inArray, or, sql } from "drizzle-orm";
5
5
 
6
6
  import type { Database } from "./database";
7
7
  import { effectiveCapabilityOwnerSql } from "./capability-components";
@@ -184,6 +184,7 @@ export async function upsertIntegrationFacetBinding(
184
184
  await addIntegrationFacetBindingOwner(db, created.id, input);
185
185
  return { row: created, changed: true };
186
186
  }
187
+ await assertPersonalBindingConnectionRebindAuthority(db, existing, input);
187
188
  if (existing.bindingKey !== input.bindingKey) {
188
189
  throw new IntegrationFacetBindingOwnershipConflictError(
189
190
  "Integration runtime identity is already assigned to another instance",
@@ -244,11 +245,70 @@ export async function upsertIntegrationFacetBinding(
244
245
  return { row, changed };
245
246
  }
246
247
 
248
+ async function assertPersonalBindingConnectionRebindAuthority(
249
+ db: Database,
250
+ existing: typeof schema.integrationFacetBindings.$inferSelect,
251
+ input: Pick<
252
+ UpsertIntegrationFacetBindingInput,
253
+ "accountId" | "workspaceId" | "connectionId" | "createdBySubjectId"
254
+ >,
255
+ ): Promise<void> {
256
+ const nextConnectionId = input.connectionId ?? null;
257
+ if (!existing.connectionId || existing.connectionId === nextConnectionId) return;
258
+ // Personal Connections are visible through the app role only to their exact
259
+ // subject, so a missing current row is an authority failure, not absence.
260
+ const [currentConnection] = await db
261
+ .select({ subjectId: schema.connections.subjectId })
262
+ .from(schema.connections)
263
+ .where(
264
+ and(
265
+ eq(schema.connections.id, existing.connectionId),
266
+ eq(schema.connections.accountId, input.accountId),
267
+ eq(schema.connections.workspaceId, input.workspaceId),
268
+ ),
269
+ )
270
+ .limit(1);
271
+ if (!currentConnection) {
272
+ throw new IntegrationFacetBindingOwnershipConflictError(
273
+ "Personal Integration instance belongs to another subject and cannot be changed in place",
274
+ );
275
+ }
276
+ if (!currentConnection.subjectId) return;
277
+ if (currentConnection.subjectId !== input.createdBySubjectId || nextConnectionId === null) {
278
+ throw new IntegrationFacetBindingOwnershipConflictError(
279
+ "Personal Integration instance belongs to another subject and cannot be changed in place",
280
+ );
281
+ }
282
+ const [nextConnection] = await db
283
+ .select({ subjectId: schema.connections.subjectId })
284
+ .from(schema.connections)
285
+ .where(
286
+ and(
287
+ eq(schema.connections.id, nextConnectionId),
288
+ eq(schema.connections.accountId, input.accountId),
289
+ eq(schema.connections.workspaceId, input.workspaceId),
290
+ ),
291
+ )
292
+ .limit(1);
293
+ if (!nextConnection || nextConnection.subjectId !== currentConnection.subjectId) {
294
+ throw new IntegrationFacetBindingOwnershipConflictError(
295
+ "Personal Integration instance Connection ownership cannot be changed",
296
+ );
297
+ }
298
+ }
299
+
247
300
  export async function listIntegrationFacetBindingOwners(
248
301
  db: Database,
249
302
  bindingId: string,
250
303
  ): Promise<IntegrationFacetBindingOwner[]> {
251
- return await loadIntegrationFacetBindingOwners(db, bindingId, true);
304
+ return (await listIntegrationFacetBindingOwnersForBindings(db, [bindingId])).get(bindingId) ?? [];
305
+ }
306
+
307
+ export async function listIntegrationFacetBindingOwnersForBindings(
308
+ db: Database,
309
+ bindingIds: string[],
310
+ ): Promise<Map<string, IntegrationFacetBindingOwner[]>> {
311
+ return await loadIntegrationFacetBindingOwnersForBindings(db, bindingIds, true);
252
312
  }
253
313
 
254
314
  async function loadIntegrationFacetBindingOwners(
@@ -256,8 +316,26 @@ async function loadIntegrationFacetBindingOwners(
256
316
  bindingId: string,
257
317
  effectiveOnly: boolean,
258
318
  ): Promise<IntegrationFacetBindingOwner[]> {
319
+ return (
320
+ (await loadIntegrationFacetBindingOwnersForBindings(db, [bindingId], effectiveOnly)).get(
321
+ bindingId,
322
+ ) ?? []
323
+ );
324
+ }
325
+
326
+ async function loadIntegrationFacetBindingOwnersForBindings(
327
+ db: Database,
328
+ bindingIds: string[],
329
+ effectiveOnly: boolean,
330
+ ): Promise<Map<string, IntegrationFacetBindingOwner[]>> {
331
+ const uniqueBindingIds = [...new Set(bindingIds)];
332
+ const ownersByBindingId = new Map(
333
+ uniqueBindingIds.map((bindingId) => [bindingId, [] as IntegrationFacetBindingOwner[]]),
334
+ );
335
+ if (uniqueBindingIds.length === 0) return ownersByBindingId;
259
336
  const rows = await db
260
337
  .select({
338
+ bindingId: schema.integrationFacetBindingOwners.bindingId,
261
339
  kind: schema.integrationFacetBindingOwners.ownerKind,
262
340
  id: schema.integrationFacetBindingOwners.ownerId,
263
341
  removable: schema.integrationFacetBindingOwners.removable,
@@ -265,7 +343,7 @@ async function loadIntegrationFacetBindingOwners(
265
343
  .from(schema.integrationFacetBindingOwners)
266
344
  .where(
267
345
  and(
268
- eq(schema.integrationFacetBindingOwners.bindingId, bindingId),
346
+ inArray(schema.integrationFacetBindingOwners.bindingId, uniqueBindingIds),
269
347
  effectiveOnly
270
348
  ? effectiveCapabilityOwnerSql(
271
349
  schema.integrationFacetBindingOwners.ownerKind,
@@ -275,10 +353,11 @@ async function loadIntegrationFacetBindingOwners(
275
353
  ),
276
354
  )
277
355
  .orderBy(
356
+ asc(schema.integrationFacetBindingOwners.bindingId),
278
357
  asc(schema.integrationFacetBindingOwners.ownerKind),
279
358
  asc(schema.integrationFacetBindingOwners.ownerId),
280
359
  );
281
- return rows.map((row) => {
360
+ for (const row of rows) {
282
361
  if (
283
362
  row.kind !== "direct" &&
284
363
  row.kind !== "plugin" &&
@@ -287,8 +366,11 @@ async function loadIntegrationFacetBindingOwners(
287
366
  ) {
288
367
  throw new Error(`Unknown Integration instance owner kind: ${row.kind}`);
289
368
  }
290
- return { kind: row.kind, id: row.id, removable: row.removable };
291
- });
369
+ ownersByBindingId
370
+ .get(row.bindingId)
371
+ ?.push({ kind: row.kind, id: row.id, removable: row.removable });
372
+ }
373
+ return ownersByBindingId;
292
374
  }
293
375
 
294
376
  export async function removeIntegrationFacetBindingOwner(