@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
@@ -23,6 +23,7 @@ import {
23
23
  type NetworkRouteConsistency as NetworkRouteConsistencyValue,
24
24
  } from "@opengeni/contracts";
25
25
  import { and, desc, eq, inArray, isNull, sql } from "drizzle-orm";
26
+ import { attachedDeviceGenerationMatches } from "./attached-browser-devices";
26
27
  import { type Database, withRlsContext } from "./database";
27
28
  import {
28
29
  advanceWorkspaceInteractionRevision,
@@ -2194,6 +2195,15 @@ export async function touchBrowserSessionController(
2194
2195
  )
2195
2196
  .limit(1);
2196
2197
  if (!observed) return false;
2198
+ if (observed.placementKind === "attached_device") {
2199
+ if (!observed.deviceId || !observed.placementInstanceId) return false;
2200
+ const current = await attachedDeviceGenerationMatches(tx, {
2201
+ workspaceId: input.workspaceId,
2202
+ deviceId: observed.deviceId,
2203
+ placementInstanceId: observed.placementInstanceId,
2204
+ });
2205
+ if (!current) return false;
2206
+ }
2197
2207
  if (observed.controllerHostSandboxGroupId) {
2198
2208
  // Match the reaper's exact lease -> holder -> BrowserSession lock
2199
2209
  // order. The pre-lock observation is only a locator; every authority
@@ -15,6 +15,7 @@ import {
15
15
  withWorkspaceSubjectRls,
16
16
  type Database,
17
17
  } from "./database";
18
+ import { connectionScopeKey } from "./connection-scopes";
18
19
  import {
19
20
  ensureIntegrationFacetDefinition,
20
21
  integrationBindingKey,
@@ -639,6 +640,176 @@ export async function listInstalledApiIntegrations(
639
640
  );
640
641
  }
641
642
 
643
+ type ApiIntegrationServerAuthorityMetadata = {
644
+ serverId: string;
645
+ connectionId: string | null;
646
+ providerDomain: string;
647
+ connectionKind: string | null;
648
+ subjectScope: "workspace" | "subject";
649
+ };
650
+
651
+ async function listInstalledApiIntegrationServerAuthorityMetadataInRlsContext(
652
+ scopedDb: Database,
653
+ workspaceId: string,
654
+ subjectId?: string,
655
+ ): Promise<ApiIntegrationServerAuthorityMetadata[]> {
656
+ const rows = await scopedDb
657
+ .select({
658
+ serverId: schema.integrationFacetBindings.runtimeKey,
659
+ connectionId: schema.integrationFacetBindings.connectionId,
660
+ providerDomain: schema.capabilityIntegrationFacets.providerDomain,
661
+ connectionKind: sql<
662
+ string | null
663
+ >`${schema.integrationFacetBindings.config} ->> 'connectionKind'`,
664
+ subjectScope: sql<
665
+ string | null
666
+ >`${schema.integrationFacetBindings.config} ->> 'subjectScope'`,
667
+ })
668
+ .from(schema.integrationFacetBindings)
669
+ .innerJoin(
670
+ schema.integrationFacetDefinitions,
671
+ eq(schema.integrationFacetDefinitions.id, schema.integrationFacetBindings.facetDefinitionId),
672
+ )
673
+ .innerJoin(
674
+ schema.capabilityIntegrationFacets,
675
+ eq(
676
+ schema.capabilityIntegrationFacets.facetId,
677
+ schema.integrationFacetDefinitions.integrationFacetId,
678
+ ),
679
+ )
680
+ .innerJoin(
681
+ schema.capabilityFacetInstallations,
682
+ eq(
683
+ schema.capabilityFacetInstallations.id,
684
+ schema.integrationFacetBindings.integrationFacetInstallationId,
685
+ ),
686
+ )
687
+ .innerJoin(
688
+ schema.capabilityPluginInstallations,
689
+ eq(
690
+ schema.capabilityPluginInstallations.id,
691
+ schema.capabilityFacetInstallations.pluginInstallationId,
692
+ ),
693
+ )
694
+ .innerJoin(
695
+ schema.capabilityApiFacets,
696
+ eq(schema.capabilityApiFacets.integrationFacetId, schema.capabilityIntegrationFacets.facetId),
697
+ )
698
+ .where(
699
+ and(
700
+ eq(schema.capabilityPluginInstallations.workspaceId, workspaceId),
701
+ eq(schema.capabilityPluginInstallations.status, "active"),
702
+ eq(schema.capabilityFacetInstallations.status, "active"),
703
+ eq(schema.integrationFacetDefinitions.kind, "tools"),
704
+ eq(schema.integrationFacetBindings.status, "active"),
705
+ sql`${schema.integrationFacetBindings.runtimeKey} is not null`,
706
+ sql`exists (
707
+ select 1 from ${schema.capabilityComponentOwners} owner
708
+ where owner.facet_installation_id = ${schema.capabilityFacetInstallations.id}
709
+ and ${effectiveCapabilityOwnerSql(sql`owner.owner_kind`, sql`owner.owner_id`)}
710
+ )`,
711
+ sql`exists (
712
+ select 1 from ${schema.integrationFacetBindingOwners} owner
713
+ where owner.binding_id = ${schema.integrationFacetBindings.id}
714
+ and ${effectiveCapabilityOwnerSql(sql`owner.owner_kind`, sql`owner.owner_id`)}
715
+ )`,
716
+ sql`exists (
717
+ select 1 from ${schema.capabilityFacetInstallations} api_installation
718
+ where api_installation.plugin_installation_id = ${schema.capabilityPluginInstallations.id}
719
+ and api_installation.facet_id = ${schema.capabilityApiFacets.facetId}
720
+ and api_installation.status = 'active'
721
+ )`,
722
+ subjectId
723
+ ? sql`(
724
+ ${schema.integrationFacetBindings.connectionId} is null
725
+ or exists (
726
+ select 1 from ${schema.connections} connection
727
+ where connection.id = ${schema.integrationFacetBindings.connectionId}
728
+ and (connection.subject_id is null or connection.subject_id = ${subjectId})
729
+ )
730
+ )`
731
+ : sql`(
732
+ ${schema.integrationFacetBindings.connectionId} is null
733
+ or exists (
734
+ select 1 from ${schema.connections} connection
735
+ where connection.id = ${schema.integrationFacetBindings.connectionId}
736
+ and connection.subject_id is null
737
+ )
738
+ )`,
739
+ ),
740
+ );
741
+ return rows.flatMap((row) =>
742
+ row.serverId
743
+ ? [
744
+ {
745
+ serverId: row.serverId,
746
+ connectionId: row.connectionId,
747
+ providerDomain: row.providerDomain,
748
+ connectionKind: row.connectionKind,
749
+ subjectScope: row.subjectScope === "subject" ? "subject" : "workspace",
750
+ },
751
+ ]
752
+ : [],
753
+ );
754
+ }
755
+
756
+ /**
757
+ * Exact runnable API-integration server ids for a scheduled service run.
758
+ * Only runtime ids and the minimum delegation-matching metadata are selected;
759
+ * no integration definition, credential, token, endpoint, or tool schema is
760
+ * materialized before incident admission.
761
+ */
762
+ export async function listInstalledApiIntegrationServerIdsForDelegations(
763
+ db: Database,
764
+ workspaceId: string,
765
+ delegations: readonly {
766
+ serverId: string;
767
+ connectionId: string;
768
+ providerDomain: string;
769
+ ownerSubjectId: string;
770
+ kind?: string | undefined;
771
+ }[],
772
+ ): Promise<string[]> {
773
+ const workspace = await withWorkspaceRls(
774
+ db,
775
+ workspaceId,
776
+ async (scopedDb) =>
777
+ await listInstalledApiIntegrationServerAuthorityMetadataInRlsContext(scopedDb, workspaceId),
778
+ );
779
+ const byServerId = new Set(workspace.map((entry) => entry.serverId));
780
+ const owners = [...new Set(delegations.map((delegation) => delegation.ownerSubjectId))];
781
+ for (const ownerSubjectId of owners) {
782
+ const ownerEntries = await withWorkspaceSubjectRls(
783
+ db,
784
+ workspaceId,
785
+ ownerSubjectId,
786
+ async (scopedDb) =>
787
+ await listInstalledApiIntegrationServerAuthorityMetadataInRlsContext(
788
+ scopedDb,
789
+ workspaceId,
790
+ ownerSubjectId,
791
+ ),
792
+ );
793
+ const exact = delegations.filter((delegation) => delegation.ownerSubjectId === ownerSubjectId);
794
+ for (const entry of ownerEntries) {
795
+ if (
796
+ entry.subjectScope === "subject" &&
797
+ entry.connectionId &&
798
+ exact.some(
799
+ (delegation) =>
800
+ delegation.serverId === entry.serverId &&
801
+ delegation.connectionId === entry.connectionId &&
802
+ delegation.providerDomain.toLowerCase() === entry.providerDomain.toLowerCase() &&
803
+ (delegation.kind ?? "") === (entry.connectionKind ?? ""),
804
+ )
805
+ ) {
806
+ byServerId.add(entry.serverId);
807
+ }
808
+ }
809
+ }
810
+ return [...byServerId];
811
+ }
812
+
642
813
  /**
643
814
  * Read installed API Integrations from a transaction that already carries the
644
815
  * workspace RLS context. This is intentionally separate from the public
@@ -1180,8 +1351,11 @@ async function loadInstallConnection(
1180
1351
  throw new Error("API Integration connection provider does not match the destination");
1181
1352
  }
1182
1353
  assertConnectionKindMatchesAuth(input.authScheme, connection.kind);
1354
+ const grantedScopes = new Set(
1355
+ connection.grantedScopes.map((scope) => connectionScopeKey(connection.providerDomain, scope)),
1356
+ );
1183
1357
  const missing = normalizedStrings(input.requiredScopes ?? [], 256).filter(
1184
- (scope) => !connection.grantedScopes.includes(scope),
1358
+ (scope) => !grantedScopes.has(connectionScopeKey(connection.providerDomain, scope)),
1185
1359
  );
1186
1360
  if (missing.length > 0) {
1187
1361
  throw new Error("API Integration connection is missing required scopes");
@@ -0,0 +1,241 @@
1
+ import {
2
+ CHILD_PAUSED_REASON_MAX_BYTES,
3
+ CHILD_PROGRESS_NOTE_MAX_BYTES,
4
+ CHILD_REQUIRES_ACTION_MAX_REQUESTS,
5
+ CHILD_REQUIRES_ACTION_PAYLOAD_MAX_BYTES,
6
+ CHILD_REQUIRES_ACTION_QUESTION_PREVIEW_MAX_BYTES,
7
+ approvalIdentifier,
8
+ type ChildPausedPayload,
9
+ type ChildProgressPayload,
10
+ type ChildRequiresActionPayload,
11
+ type ChildRequiresActionRequest,
12
+ type ChildRequiresActionResolvedPayload,
13
+ type ChildWaitingCapacityPayload,
14
+ type HumanInputQuestion,
15
+ } from "@opengeni/contracts";
16
+
17
+ /**
18
+ * Child lifecycle notices (`child_requires_action`,
19
+ * `child_requires_action_resolved`, `child_paused`, `child_waiting_capacity`,
20
+ * `child_progress`) are produced only while this process-global flag is on.
21
+ * The deployment value is `OPENGENI_CHILD_LIFECYCLE_NOTICES_ENABLED`, parsed
22
+ * once at boot by `@opengeni/config` (`settings.childLifecycleNoticesEnabled`)
23
+ * and installed here by the API app and both worker roles. Rolling hazard: a
24
+ * worker from before these kinds existed throws in `mapSessionSystemUpdate`
25
+ * on an unknown kind, so the flag stays off until the whole fleet runs a new
26
+ * image. Delivery and consumption of an already committed notice never read
27
+ * the flag; only production does.
28
+ */
29
+ let childLifecycleNoticesEnabledFlag = false;
30
+
31
+ export function configureChildLifecycleNotices(input: { enabled: boolean }): void {
32
+ childLifecycleNoticesEnabledFlag = input.enabled === true;
33
+ }
34
+
35
+ export function childLifecycleNoticesEnabled(): boolean {
36
+ return childLifecycleNoticesEnabledFlag;
37
+ }
38
+
39
+ /** Bounded UTF-8 prefix with an explicit marker; never splits a code point. */
40
+ export function boundedChildNoticeText(
41
+ value: string,
42
+ maxBytes: number,
43
+ ): { text: string; truncated: boolean } {
44
+ const encoder = new TextEncoder();
45
+ const bytes = encoder.encode(value);
46
+ if (bytes.byteLength <= maxBytes) return { text: value, truncated: false };
47
+ const marker = "…";
48
+ const budget = Math.max(0, maxBytes - encoder.encode(marker).byteLength);
49
+ let end = Math.min(budget, bytes.byteLength);
50
+ while (end > 0 && end < bytes.byteLength && (bytes[end]! & 0xc0) === 0x80) end -= 1;
51
+ return { text: `${new TextDecoder().decode(bytes.subarray(0, end))}${marker}`, truncated: true };
52
+ }
53
+
54
+ export const CHILD_LIFECYCLE_DEDUPE_PREFIX = {
55
+ requiresAction: "child-requires-action",
56
+ requiresActionResolved: "child-requires-action-resolved",
57
+ paused: "child-paused",
58
+ waitingCapacity: "child-waiting-capacity",
59
+ progress: "child-progress",
60
+ } as const;
61
+
62
+ export function childRequiresActionDedupeKey(input: {
63
+ childSessionId: string;
64
+ turnId: string;
65
+ turnGeneration: number;
66
+ }): string {
67
+ return `${CHILD_LIFECYCLE_DEDUPE_PREFIX.requiresAction}:${input.childSessionId}:${input.turnId}:${input.turnGeneration}`;
68
+ }
69
+
70
+ export function childRequiresActionResolvedDedupeKey(input: {
71
+ childSessionId: string;
72
+ turnId: string;
73
+ turnGeneration: number;
74
+ requestId: string | null;
75
+ approvalId: string | null;
76
+ }): string {
77
+ return `${CHILD_LIFECYCLE_DEDUPE_PREFIX.requiresActionResolved}:${input.childSessionId}:${input.turnId}:${input.turnGeneration}:${input.requestId ?? input.approvalId ?? "unknown"}`;
78
+ }
79
+
80
+ export function childPausedDedupeKey(input: { childSessionId: string; receiptId: string }): string {
81
+ return `${CHILD_LIFECYCLE_DEDUPE_PREFIX.paused}:${input.childSessionId}:${input.receiptId}`;
82
+ }
83
+
84
+ export function childWaitingCapacityDedupeKey(input: {
85
+ childSessionId: string;
86
+ waiterId: string;
87
+ }): string {
88
+ return `${CHILD_LIFECYCLE_DEDUPE_PREFIX.waitingCapacity}:${input.childSessionId}:${input.waiterId}`;
89
+ }
90
+
91
+ export function childProgressDedupeKey(input: {
92
+ childSessionId: string;
93
+ receiptId: string;
94
+ }): string {
95
+ return `${CHILD_LIFECYCLE_DEDUPE_PREFIX.progress}:${input.childSessionId}:${input.receiptId}`;
96
+ }
97
+
98
+ /** Bounded, secret-free tool name of a serialized SDK approval interruption. */
99
+ function approvalToolName(value: unknown): string | null {
100
+ if (!value || typeof value !== "object") return null;
101
+ const approval = value as Record<string, unknown>;
102
+ const rawItem =
103
+ approval.rawItem && typeof approval.rawItem === "object"
104
+ ? (approval.rawItem as Record<string, unknown>)
105
+ : null;
106
+ const candidate = rawItem?.name ?? approval.name;
107
+ return typeof candidate === "string" && candidate.length > 0
108
+ ? boundedChildNoticeText(candidate, 128).text
109
+ : null;
110
+ }
111
+
112
+ /**
113
+ * Build the bounded child_requires_action payload from the child's frozen
114
+ * human-input requests and pending approvals. Only typed, content-free facts
115
+ * plus one bounded first-question preview per request cross to the parent:
116
+ * never subject ids, credentials, or raw tool arguments. The whole payload
117
+ * stays within CHILD_REQUIRES_ACTION_PAYLOAD_MAX_BYTES by dropping trailing
118
+ * requests and recording `truncated`.
119
+ */
120
+ export function buildChildRequiresActionPayload(input: {
121
+ childSessionId: string;
122
+ childTurnId: string;
123
+ childTurnGeneration: number;
124
+ humanInputRequests: ReadonlyArray<{
125
+ id: string;
126
+ questions: readonly HumanInputQuestion[];
127
+ allowSkip: boolean;
128
+ expiresAt?: Date | null;
129
+ }>;
130
+ pendingApprovals: readonly unknown[];
131
+ }): ChildRequiresActionPayload {
132
+ const requests: ChildRequiresActionRequest[] = [];
133
+ for (const request of input.humanInputRequests) {
134
+ const first = request.questions[0];
135
+ requests.push({
136
+ kind: "human_input",
137
+ requestId: request.id,
138
+ questionCount: request.questions.length,
139
+ firstQuestion: boundedChildNoticeText(
140
+ typeof first?.prompt === "string" ? first.prompt : "",
141
+ CHILD_REQUIRES_ACTION_QUESTION_PREVIEW_MAX_BYTES,
142
+ ).text,
143
+ allowSkip: request.allowSkip,
144
+ expiresAt: request.expiresAt ? request.expiresAt.toISOString() : null,
145
+ });
146
+ }
147
+ const seenApprovals = new Set<string>();
148
+ for (const pending of input.pendingApprovals) {
149
+ const approvalId = approvalIdentifier(pending);
150
+ if (!approvalId || seenApprovals.has(approvalId)) continue;
151
+ seenApprovals.add(approvalId);
152
+ requests.push({
153
+ kind: "approval",
154
+ approvalId: boundedChildNoticeText(approvalId, 256).text,
155
+ toolName: approvalToolName(pending),
156
+ });
157
+ }
158
+ let truncated = requests.length > CHILD_REQUIRES_ACTION_MAX_REQUESTS;
159
+ let bounded = requests.slice(0, CHILD_REQUIRES_ACTION_MAX_REQUESTS);
160
+ const build = (): ChildRequiresActionPayload => ({
161
+ type: "child_requires_action",
162
+ childSessionId: input.childSessionId,
163
+ childTurnId: input.childTurnId,
164
+ childTurnGeneration: input.childTurnGeneration,
165
+ requests: bounded,
166
+ truncated,
167
+ });
168
+ let payload = build();
169
+ while (
170
+ bounded.length > 0 &&
171
+ Buffer.byteLength(JSON.stringify(payload)) > CHILD_REQUIRES_ACTION_PAYLOAD_MAX_BYTES
172
+ ) {
173
+ bounded = bounded.slice(0, -1);
174
+ truncated = true;
175
+ payload = build();
176
+ }
177
+ return payload;
178
+ }
179
+
180
+ export function childRequiresActionSummary(
181
+ childSessionId: string,
182
+ payload: ChildRequiresActionPayload,
183
+ ): string {
184
+ const humanInputs = payload.requests.filter((request) => request.kind === "human_input");
185
+ const approvals = payload.requests.filter((request) => request.kind === "approval");
186
+ const first = humanInputs[0];
187
+ const lines = [
188
+ `Worker ${childSessionId} is blocked and needs input (turn ${payload.childTurnId}).`,
189
+ ];
190
+ if (first && first.kind === "human_input") {
191
+ lines.push(
192
+ `It asked: ${first.firstQuestion}${first.questionCount > 1 ? ` (+${first.questionCount - 1} more question${first.questionCount > 2 ? "s" : ""})` : ""}${humanInputs.length > 1 ? ` [${humanInputs.length} open requests]` : ""}.`,
193
+ );
194
+ }
195
+ if (approvals.length > 0) {
196
+ const names = approvals
197
+ .map((request) => (request.kind === "approval" ? request.toolName : null))
198
+ .filter((name): name is string => name !== null);
199
+ lines.push(
200
+ `${approvals.length} tool approval${approvals.length > 1 ? "s are" : " is"} waiting for a human${names.length > 0 ? ` (${names.join(", ")})` : ""}.`,
201
+ );
202
+ }
203
+ if (payload.truncated) lines.push("The request list was truncated.");
204
+ return lines.join(" ");
205
+ }
206
+
207
+ export function childRequiresActionResolvedSummary(
208
+ childSessionId: string,
209
+ payload: ChildRequiresActionResolvedPayload,
210
+ ): string {
211
+ const subject = payload.requestId ? "input request" : "approval";
212
+ return `Worker ${childSessionId}: its ${subject} was ${payload.outcome} (by ${payload.respondedByKind.replace("_", " ")}); the worker is no longer blocked on it.`;
213
+ }
214
+
215
+ export function childPausedSummary(childSessionId: string, payload: ChildPausedPayload): string {
216
+ const base = `Worker ${childSessionId} was paused by ${payload.actorKind === "agent" ? "an agent" : payload.actorKind === "api" ? "the API" : "a human"}.`;
217
+ return payload.reason ? `${base} Reason: ${payload.reason}` : base;
218
+ }
219
+
220
+ export function childWaitingCapacitySummary(
221
+ childSessionId: string,
222
+ payload: ChildWaitingCapacityPayload,
223
+ ): string {
224
+ return `Worker ${childSessionId} is waiting for ${payload.provider === "codex" ? "Codex" : "xAI"} provider capacity${payload.nextCheckAt ? ` (next check ${payload.nextCheckAt})` : ""}; its turn resumes automatically.`;
225
+ }
226
+
227
+ export function childProgressSummary(
228
+ childSessionId: string,
229
+ payload: ChildProgressPayload,
230
+ ): string {
231
+ return `Worker ${childSessionId} progress: ${payload.progressNote}`;
232
+ }
233
+
234
+ export function boundedChildPausedReason(reason: string | null | undefined): string | null {
235
+ if (reason === null || reason === undefined || reason.length === 0) return null;
236
+ return boundedChildNoticeText(reason, CHILD_PAUSED_REASON_MAX_BYTES).text;
237
+ }
238
+
239
+ export function boundedChildProgressNote(note: string): string {
240
+ return boundedChildNoticeText(note, CHILD_PROGRESS_NOTE_MAX_BYTES).text;
241
+ }
@@ -55,15 +55,6 @@ export class CodemodeToolApprovalRequiredError extends Error {
55
55
  }
56
56
  }
57
57
 
58
- export class CodemodeCallBudgetExceededError extends Error {
59
- readonly code = "codemode_call_budget_exhausted";
60
-
61
- constructor() {
62
- super("Codemode call budget is exhausted for this turn");
63
- this.name = "CodemodeCallBudgetExceededError";
64
- }
65
- }
66
-
67
58
  export class CodemodePayloadTooLargeError extends Error {
68
59
  readonly code = "codemode_payload_too_large";
69
60
 
@@ -81,13 +72,12 @@ export type SubmitCodemodeOperationInput = {
81
72
  attemptId: string;
82
73
  executionGeneration: number;
83
74
  call: AttemptToolCallValue;
84
- callLimit: number;
85
75
  };
86
76
 
87
77
  /**
88
78
  * Bind one caller-provided operation id to one exact catalog call. Duplicate
89
79
  * submission of identical bytes is free and returns the same row; repurposing
90
- * the id fails. Admission, budget reservation and insert share one turn lock.
80
+ * the id fails. Admission and insert share one turn lock.
91
81
  */
92
82
  export async function submitCodemodeOperation(
93
83
  db: Database,
@@ -120,7 +110,6 @@ export async function submitCodemodeOperation(
120
110
  status: schema.sessionTurns.status,
121
111
  activeAttemptId: schema.sessionTurns.activeAttemptId,
122
112
  executionGeneration: schema.sessionTurns.executionGeneration,
123
- callCount: schema.sessionTurns.codemodeCallCount,
124
113
  })
125
114
  .from(schema.sessionTurns)
126
115
  .where(
@@ -141,10 +130,6 @@ export async function submitCodemodeOperation(
141
130
  ) {
142
131
  throw new CodemodeOperationNotExecutableError();
143
132
  }
144
- if (!Number.isInteger(input.callLimit) || input.callLimit < 1) {
145
- throw new CodemodeCallBudgetExceededError();
146
- }
147
- if (turn.callCount >= input.callLimit) throw new CodemodeCallBudgetExceededError();
148
133
 
149
134
  const [catalogRow] = await tx
150
135
  .select({ catalog: schema.sessionAttemptToolCatalogs.catalog })
@@ -180,25 +165,6 @@ export async function submitCodemodeOperation(
180
165
  throw new CodemodeToolApprovalRequiredError();
181
166
  }
182
167
 
183
- const [counter] = await tx
184
- .update(schema.sessionTurns)
185
- .set({
186
- codemodeCallCount: sql`${schema.sessionTurns.codemodeCallCount} + 1`,
187
- updatedAt: new Date(),
188
- })
189
- .where(
190
- and(
191
- eq(schema.sessionTurns.workspaceId, input.workspaceId),
192
- eq(schema.sessionTurns.id, input.turnId),
193
- eq(schema.sessionTurns.status, "running"),
194
- eq(schema.sessionTurns.activeAttemptId, input.attemptId),
195
- eq(schema.sessionTurns.executionGeneration, input.executionGeneration),
196
- sql`${schema.sessionTurns.codemodeCallCount} < ${input.callLimit}`,
197
- ),
198
- )
199
- .returning({ count: schema.sessionTurns.codemodeCallCount });
200
- if (!counter) throw new CodemodeCallBudgetExceededError();
201
-
202
168
  const [created] = await tx
203
169
  .insert(schema.sessionAttemptCodemodeCalls)
204
170
  .values({
@@ -0,0 +1,100 @@
1
+ import {
2
+ boolean,
3
+ index,
4
+ integer,
5
+ jsonb,
6
+ pgTable,
7
+ text,
8
+ timestamp,
9
+ uniqueIndex,
10
+ uuid,
11
+ } from "drizzle-orm/pg-core";
12
+ import type { CompanyBrainMemorySelectionReference } from "./company-brain-context-selection";
13
+
14
+ /**
15
+ * Content-free accepted-logical-turn selection receipt. Migration 0259 owns
16
+ * the exact-attempt writer, immutable trigger, FORCE-RLS policy, and grants.
17
+ */
18
+ export const companyBrainContextSelectionReceipts = pgTable(
19
+ "company_brain_context_selection_receipts",
20
+ {
21
+ id: uuid("id").primaryKey().defaultRandom(),
22
+ accountId: uuid("account_id").notNull(),
23
+ workspaceId: uuid("workspace_id").notNull(),
24
+ sessionId: uuid("session_id").notNull(),
25
+ rootSessionId: uuid("root_session_id").notNull(),
26
+ turnId: uuid("turn_id").notNull(),
27
+ createdByAttemptId: uuid("created_by_attempt_id").notNull(),
28
+ createdByExecutionGeneration: integer("created_by_execution_generation").notNull(),
29
+ acceptedAt: timestamp("accepted_at", { withTimezone: true }).notNull(),
30
+ sessionRole: text("session_role").notNull(),
31
+ memoryEnabled: boolean("memory_enabled").notNull(),
32
+ memoryPromptMode: text("memory_prompt_mode").notNull(),
33
+ companyProfileIncluded: boolean("company_profile_included").notNull(),
34
+ instructionPolicyEntryHash: text("instruction_policy_entry_hash").notNull(),
35
+ preferenceDescriptorHash: text("preference_descriptor_hash"),
36
+ companyProfileSnapshotHash: text("company_profile_snapshot_hash").notNull(),
37
+ turnContextSnapshotId: uuid("turn_context_snapshot_id").notNull(),
38
+ turnContextSnapshotHash: text("turn_context_snapshot_hash").notNull(),
39
+ turnContextSnapshotSource: text("turn_context_snapshot_source").notNull(),
40
+ memorySelections: jsonb("memory_selections")
41
+ .$type<readonly CompanyBrainMemorySelectionReference[]>()
42
+ .notNull(),
43
+ renderedMemorySelections: jsonb("rendered_memory_selections")
44
+ .$type<readonly CompanyBrainMemorySelectionReference[]>()
45
+ .notNull(),
46
+ selectionHash: text("selection_hash").notNull(),
47
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
48
+ },
49
+ (table) => ({
50
+ logicalTurn: uniqueIndex("company_brain_context_selection_turn_uq").on(
51
+ table.workspaceId,
52
+ table.turnId,
53
+ ),
54
+ workspaceTimeline: index("company_brain_context_selection_workspace_time_idx").on(
55
+ table.workspaceId,
56
+ table.createdAt,
57
+ table.id,
58
+ ),
59
+ }),
60
+ );
61
+
62
+ /**
63
+ * Exact bounded accepted-turn authority that may contain the legacy workspace
64
+ * instruction fallback. It is deliberately separate from the content-free
65
+ * selection receipt and has no direct runtime table privileges.
66
+ */
67
+ export const companyBrainTurnContextSnapshots = pgTable(
68
+ "company_brain_turn_context_snapshots",
69
+ {
70
+ id: uuid("id").primaryKey().defaultRandom(),
71
+ accountId: uuid("account_id").notNull(),
72
+ workspaceId: uuid("workspace_id").notNull(),
73
+ sessionId: uuid("session_id").notNull(),
74
+ rootSessionId: uuid("root_session_id").notNull(),
75
+ turnId: uuid("turn_id").notNull(),
76
+ acceptedAt: timestamp("accepted_at", { withTimezone: true }).notNull(),
77
+ memoryEnabled: boolean("memory_enabled").notNull(),
78
+ memoryPromptMode: text("memory_prompt_mode").notNull(),
79
+ legacyWorkspaceInstructions: text("legacy_workspace_instructions"),
80
+ legacyWorkspaceInstructionsOriginalUtf8Bytes: integer(
81
+ "legacy_workspace_instructions_original_utf8_bytes",
82
+ ),
83
+ legacyWorkspaceInstructionsTruncated: boolean(
84
+ "legacy_workspace_instructions_truncated",
85
+ ).notNull(),
86
+ snapshotSource: text("snapshot_source").notNull(),
87
+ snapshotHash: text("snapshot_hash").notNull(),
88
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
89
+ },
90
+ (table) => ({
91
+ logicalTurn: uniqueIndex("company_brain_turn_context_snapshot_turn_uq").on(
92
+ table.workspaceId,
93
+ table.turnId,
94
+ ),
95
+ workspaceIdentity: uniqueIndex("company_brain_turn_context_snapshot_workspace_id_uq").on(
96
+ table.workspaceId,
97
+ table.id,
98
+ ),
99
+ }),
100
+ );