@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
@@ -3,6 +3,7 @@ import {
3
3
  XaiProviderAccountAuthoritySnapshotV1,
4
4
  DraftTimelineAnnotations,
5
5
  McpPersonalConnectionDelegations,
6
+ PersonalResourceAttachmentSummary,
6
7
  TimelineAnnotations,
7
8
  metadataWithTurnExecutionPolicyV1,
8
9
  mergeResourceRefs,
@@ -19,15 +20,17 @@ import {
19
20
  type SandboxOs,
20
21
  type SessionEvent,
21
22
  type SessionEventType,
23
+ type SessionPromptRouting,
22
24
  type SessionTurn,
23
25
  type SessionTurnSource,
24
26
  type SessionTurnStatus,
25
27
  type ToolRef,
26
28
  type TurnExecutionPolicyV1,
27
29
  type TimelineAnnotation,
30
+ type PersonalResourceAttachmentIntent,
28
31
  } from "@opengeni/contracts";
29
32
  import { and, asc, eq, inArray, sql } from "drizzle-orm";
30
- import type { Database, SessionActivityDatabase } from "./database";
33
+ import { setSubjectRlsContext, type Database, type SessionActivityDatabase } from "./database";
31
34
  import {
32
35
  fromPostgresLosslessJson,
33
36
  fromPostgresLosslessText,
@@ -42,6 +45,7 @@ import {
42
45
  evaluateSessionControl,
43
46
  lockSessionEventWriteRows,
44
47
  lockWorkspaceInferenceControl,
48
+ lockWorkspaceInferenceControlForAdmission,
45
49
  registerInternalUpdateWakeInTransaction,
46
50
  reserveSessionCommandReceipt,
47
51
  registerSessionWorkflowWakeInTransaction,
@@ -52,6 +56,7 @@ import {
52
56
  updateSessionCommandReceiptResult,
53
57
  } from "./session-control";
54
58
  import { sessionRealtimeIsActiveInTransaction } from "./session-realtime-state";
59
+ import { autoResumeGoalPausedByCapInTransaction } from "./session-goal-pacing";
55
60
  import {
56
61
  mirrorSessionRealtimeContextInTransaction,
57
62
  renderRealtimeHumanInputContext,
@@ -65,6 +70,8 @@ import {
65
70
  type FrozenTurnInitiator,
66
71
  } from "./turn-initiator";
67
72
  import { resolveXaiProviderAccountAuthoritySnapshotForAcceptanceInTransaction } from "./xai-subscription";
73
+ import { assertActiveManagedHumanOrganizationMembership } from "./organization-membership-lifecycle";
74
+ import { acceptTurnPersonalResourceAttachmentInTransaction } from "./user-resource-authority";
68
75
 
69
76
  type SessionEventInsertWithPayload = typeof schema.sessionEvents.$inferInsert & {
70
77
  payload: unknown;
@@ -77,7 +84,8 @@ export type QueueCommandConflictCode =
77
84
  | "PROMPT_CHANGED"
78
85
  | "DRAFT_CHANGED"
79
86
  | "DRAFT_NOT_EMPTY"
80
- | "EDIT_SOURCE_CHANGED";
87
+ | "EDIT_SOURCE_CHANGED"
88
+ | "ONCE_ATTACHMENT_IMMUTABLE";
81
89
 
82
90
  export class QueueCommandConflictError extends Error {
83
91
  readonly name = "QueueCommandConflictError";
@@ -113,14 +121,19 @@ export type EditQueueCommandResult = QueueCommandResult & {
113
121
  export type SteerQueueCommandResult = QueueCommandResult & {
114
122
  interruptionCount: number;
115
123
  workspaceControlEventId: string | null;
124
+ workflowId: string;
125
+ wakeRevision: number;
116
126
  };
117
127
 
118
128
  export type SubmitHumanPromptResult = {
119
129
  receipt: SessionCommandReceiptRow;
130
+ routing: SessionPromptRouting;
120
131
  queueVersion: number;
121
132
  accepted: SessionEvent;
122
133
  events: SessionEvent[];
123
134
  turn: SessionTurn;
135
+ /** Current actor/session composer after an exact draft-bound submission. */
136
+ draft: ComposerDraftRow | null;
124
137
  /** Backward-compatible row identities for lower-level callers. */
125
138
  acceptedEventId: string;
126
139
  eventIds: string[];
@@ -170,7 +183,7 @@ function mapSubmittedPromptTurn(row: typeof schema.sessionTurns.$inferSelect): S
170
183
  toolsProvided: row.toolsProvided,
171
184
  model: row.model,
172
185
  reasoningEffort: row.reasoningEffort as ReasoningEffort,
173
- latencyMode: (row.latencyMode as LatencyMode | null | undefined) ?? "standard",
186
+ latencyMode: row.latencyMode as LatencyMode,
174
187
  sandboxBackend: row.sandboxBackend as SandboxBackend,
175
188
  sandboxOs: (row.sandboxOs as SandboxOs | null) ?? null,
176
189
  metadata: row.metadata,
@@ -185,6 +198,9 @@ function mapSubmittedPromptTurn(row: typeof schema.sessionTurns.$inferSelect): S
185
198
  ),
186
199
  initiatorContext: row.initiatorContext ?? {},
187
200
  personalConnections,
201
+ personalResources: row.personalResourceAttachmentSummary
202
+ ? PersonalResourceAttachmentSummary.parse(row.personalResourceAttachmentSummary)
203
+ : null,
188
204
  cancelledBy: row.cancelledBy,
189
205
  cancelReason: row.cancelReason,
190
206
  startedAt: row.startedAt ? row.startedAt.toISOString() : null,
@@ -211,9 +227,16 @@ async function personalConnectionDelegationsForAgentActor(
211
227
  db: Database,
212
228
  workspaceId: string,
213
229
  actor: Extract<SessionCommandActor, { type: "agent_attempt" }>,
214
- ): Promise<McpPersonalConnectionDelegation[]> {
230
+ targetSessionId: string,
231
+ ): Promise<{
232
+ delegations: McpPersonalConnectionDelegation[];
233
+ connectionAuthoritySubjectId: string | null;
234
+ }> {
215
235
  const [row] = await db
216
- .select({ delegations: schema.sessionTurns.personalConnectionDelegations })
236
+ .select({
237
+ delegations: schema.sessionTurns.personalConnectionDelegations,
238
+ initiatingHumanSubjectId: schema.sessionTurns.initiatingHumanSubjectId,
239
+ })
217
240
  .from(schema.sessionTurns)
218
241
  .where(
219
242
  and(
@@ -234,7 +257,37 @@ async function personalConnectionDelegationsForAgentActor(
234
257
  `Agent authority turn has malformed personal MCP delegation state: ${actor.sessionId}/${actor.turnId}`,
235
258
  );
236
259
  }
237
- return parsed.data.map((delegation) => ({ ...delegation }));
260
+ // Agent messages and Agent Steer create new accepted work. A once grant is
261
+ // already bound to the caller turn, while a session grant may be projected
262
+ // only into another turn of that exact session. Always grants can cross the
263
+ // direct parent/child boundary subject to the live admission fences.
264
+ const delegations = parsed.data
265
+ .filter((delegation) => {
266
+ const authority = delegation.userDelegation;
267
+ if (!authority) return true;
268
+ if (authority.mode === "once") return false;
269
+ if (authority.mode === "session") return authority.sessionId === targetSessionId;
270
+ return true;
271
+ })
272
+ .map((delegation) => ({ ...delegation }));
273
+ const activated = delegations.filter((delegation) => delegation.userDelegation);
274
+ if (activated.length === 0) {
275
+ return { delegations, connectionAuthoritySubjectId: null };
276
+ }
277
+ const connectionAuthoritySubjectId = row.initiatingHumanSubjectId;
278
+ if (!connectionAuthoritySubjectId) {
279
+ throw new SessionControlInvariantError(
280
+ `Agent authority turn lost its causal human: ${actor.sessionId}/${actor.turnId}`,
281
+ );
282
+ }
283
+ for (const delegation of activated) {
284
+ if (delegation.ownerSubjectId !== connectionAuthoritySubjectId) {
285
+ throw new SessionControlInvariantError(
286
+ `Agent authority turn causal human does not own retained connection authority: ${actor.sessionId}/${actor.turnId}`,
287
+ );
288
+ }
289
+ }
290
+ return { delegations, connectionAuthoritySubjectId };
238
291
  }
239
292
 
240
293
  async function xaiAuthorityForAgentActor(
@@ -664,10 +717,19 @@ export async function saveComposerDraftInTransaction(
664
717
  resources: ResourceRef[];
665
718
  model: string;
666
719
  reasoningEffort: ReasoningEffort;
667
- latencyMode?: LatencyMode;
720
+ latencyMode: LatencyMode;
721
+ /**
722
+ * Bound the workspace control prefix wait (request-scoped API callers pass
723
+ * `workspaceControlRequestLockTimeoutMs()`); omit for lifecycle callers.
724
+ */
725
+ controlLockTimeoutMs?: number;
668
726
  },
669
727
  ): Promise<ComposerDraftRow> {
670
- await lockWorkspaceInferenceControl(db, input.workspaceId, "share");
728
+ await lockWorkspaceInferenceControl(db, input.workspaceId, "share", {
729
+ ...(input.controlLockTimeoutMs !== undefined
730
+ ? { lockTimeoutMs: input.controlLockTimeoutMs }
731
+ : {}),
732
+ });
671
733
  await lockSessionEventWriteRows(db, {
672
734
  workspaceId: input.workspaceId,
673
735
  controlLock: "already_locked",
@@ -698,7 +760,7 @@ export async function saveComposerDraftInTransaction(
698
760
  toolsProvided: false,
699
761
  model: input.model,
700
762
  reasoningEffort: input.reasoningEffort,
701
- latencyMode: input.latencyMode ?? "standard",
763
+ latencyMode: input.latencyMode,
702
764
  // A queue edit is still the same accepted work item. Preserve its frozen
703
765
  // initiator through arbitrary draft saves; only a genuinely new compose or
704
766
  // Steer captures the submitting actor.
@@ -728,9 +790,18 @@ export async function moveQueuedTurnInTransaction(
728
790
  expectedQueueVersion: number;
729
791
  actor: SessionCommandActor;
730
792
  operationKey: string;
793
+ /**
794
+ * Bound the workspace control prefix wait (request-scoped API callers pass
795
+ * `workspaceControlRequestLockTimeoutMs()`); omit for lifecycle callers.
796
+ */
797
+ controlLockTimeoutMs?: number;
731
798
  },
732
799
  ): Promise<QueueCommandResult> {
733
- await lockWorkspaceInferenceControl(db, input.workspaceId, "share");
800
+ await lockWorkspaceInferenceControl(db, input.workspaceId, "share", {
801
+ ...(input.controlLockTimeoutMs !== undefined
802
+ ? { lockTimeoutMs: input.controlLockTimeoutMs }
803
+ : {}),
804
+ });
734
805
  await lockSessionEventWriteRows(db, {
735
806
  workspaceId: input.workspaceId,
736
807
  controlLock: "already_locked",
@@ -864,9 +935,18 @@ export async function deleteSessionQueueItemInTransaction(
864
935
  actor: SessionCommandActor;
865
936
  operationKey: string;
866
937
  reason?: string | null;
938
+ /**
939
+ * Bound the workspace control prefix wait (request-scoped API callers pass
940
+ * `workspaceControlRequestLockTimeoutMs()`); omit for lifecycle callers.
941
+ */
942
+ controlLockTimeoutMs?: number;
867
943
  },
868
944
  ): Promise<QueueCommandResult> {
869
- await lockWorkspaceInferenceControl(db, input.workspaceId, "share");
945
+ await lockWorkspaceInferenceControl(db, input.workspaceId, "share", {
946
+ ...(input.controlLockTimeoutMs !== undefined
947
+ ? { lockTimeoutMs: input.controlLockTimeoutMs }
948
+ : {}),
949
+ });
870
950
  await lockSessionEventWriteRows(db, {
871
951
  workspaceId: input.workspaceId,
872
952
  controlLock: "already_locked",
@@ -997,9 +1077,18 @@ export async function editQueuedTurnInTransaction(
997
1077
  replaceDraft: boolean;
998
1078
  actor: SessionCommandActor;
999
1079
  operationKey: string;
1080
+ /**
1081
+ * Bound the workspace control prefix wait (request-scoped API callers pass
1082
+ * `workspaceControlRequestLockTimeoutMs()`); omit for lifecycle callers.
1083
+ */
1084
+ controlLockTimeoutMs?: number;
1000
1085
  },
1001
1086
  ): Promise<EditQueueCommandResult> {
1002
- await lockWorkspaceInferenceControl(db, input.workspaceId, "share");
1087
+ await lockWorkspaceInferenceControl(db, input.workspaceId, "share", {
1088
+ ...(input.controlLockTimeoutMs !== undefined
1089
+ ? { lockTimeoutMs: input.controlLockTimeoutMs }
1090
+ : {}),
1091
+ });
1003
1092
  await lockSessionEventWriteRows(db, {
1004
1093
  workspaceId: input.workspaceId,
1005
1094
  controlLock: "already_locked",
@@ -1070,6 +1159,17 @@ export async function editQueuedTurnInTransaction(
1070
1159
  draftRevision,
1071
1160
  });
1072
1161
  }
1162
+ if (turn.personalResourceAttachmentSummary?.mode === "once") {
1163
+ throw new QueueCommandConflictError(
1164
+ "ONCE_ATTACHMENT_IMMUTABLE",
1165
+ "A queued prompt with a once personal-resource attachment cannot be edited into new work",
1166
+ {
1167
+ queueVersion: session.queueVersion,
1168
+ turnVersion: turn.version,
1169
+ draftRevision,
1170
+ },
1171
+ );
1172
+ }
1073
1173
  const nextDraftRevision = draftRevision + 1;
1074
1174
  const draftValues = {
1075
1175
  accountId: input.accountId,
@@ -1177,9 +1277,19 @@ export async function steerQueuedTurnInTransaction(
1177
1277
  controlEtag?: string | null;
1178
1278
  actor: SessionCommandActor;
1179
1279
  operationKey: string;
1280
+ /** Request-scoped callers bound the control prefix wait; lifecycle callers omit it. */
1281
+ controlLockTimeoutMs?: number;
1180
1282
  },
1181
1283
  ): Promise<SteerQueueCommandResult> {
1182
- const workspaceControl = await lockWorkspaceInferenceControl(db, input.workspaceId, "update");
1284
+ const admission = await lockWorkspaceInferenceControlForAdmission(db, {
1285
+ workspaceId: input.workspaceId,
1286
+ sessionId: input.sessionId,
1287
+ resumePausedBranch: true,
1288
+ ...(input.controlLockTimeoutMs !== undefined
1289
+ ? { lockTimeoutMs: input.controlLockTimeoutMs }
1290
+ : {}),
1291
+ });
1292
+ const workspaceControl = admission.control;
1183
1293
  await lockSessionEventWriteRows(db, {
1184
1294
  workspaceId: input.workspaceId,
1185
1295
  controlLock: "already_locked",
@@ -1207,6 +1317,10 @@ export async function steerQueuedTurnInTransaction(
1207
1317
  });
1208
1318
  if (reserved.replay && reserved.receipt.appliedQueueVersion !== null) {
1209
1319
  const replaySession = await lockSession(db, input.workspaceId, input.sessionId);
1320
+ const wakeRevision = Number(reserved.receipt.result.wakeRevision ?? 0);
1321
+ if (wakeRevision < 1) {
1322
+ throw new SessionControlInvariantError("Replayed queue Steer receipt has no wake revision");
1323
+ }
1210
1324
  return {
1211
1325
  receipt: reserved.receipt,
1212
1326
  queueVersion: replaySession.queueVersion,
@@ -1217,6 +1331,8 @@ export async function steerQueuedTurnInTransaction(
1217
1331
  typeof reserved.receipt.result.workspaceControlEventId === "string"
1218
1332
  ? reserved.receipt.result.workspaceControlEventId
1219
1333
  : null,
1334
+ workflowId: replaySession.temporalWorkflowId ?? `session-${input.sessionId}`,
1335
+ wakeRevision,
1220
1336
  replay: true,
1221
1337
  };
1222
1338
  }
@@ -1244,6 +1360,7 @@ export async function steerQueuedTurnInTransaction(
1244
1360
  : input.actor.subjectId,
1245
1361
  reason: "human_steer",
1246
1362
  observedControlEtag: input.controlEtag ?? null,
1363
+ admission,
1247
1364
  });
1248
1365
  const session = await lockSession(db, input.workspaceId, input.sessionId);
1249
1366
  const rows = await loadQueuedTurns(db, input.workspaceId, input.sessionId, true);
@@ -1395,6 +1512,8 @@ export async function steerQueuedTurnInTransaction(
1395
1512
  eventIds: eventRows.map((event) => event.id),
1396
1513
  interruptionCount,
1397
1514
  workspaceControlEventId: resumed.workspaceControlEventId,
1515
+ workflowId: session.temporalWorkflowId ?? `session-${input.sessionId}`,
1516
+ wakeRevision,
1398
1517
  replay: false,
1399
1518
  };
1400
1519
  }
@@ -1434,14 +1553,34 @@ export async function submitHumanPromptInTransaction(
1434
1553
  /** Record the admitted run's durable usage fact in this transaction. */
1435
1554
  recordAgentRunUsage?: boolean;
1436
1555
  personalConnectionDelegations?: McpPersonalConnectionDelegation[];
1556
+ personalResourceAttachment?: PersonalResourceAttachmentIntent;
1437
1557
  mcpCredentialUpdates?: Array<{
1438
1558
  id: string;
1439
1559
  headersEncrypted: Record<string, string>;
1440
1560
  }>;
1561
+ /** Request-scoped callers bound the control prefix wait; lifecycle callers omit it. */
1562
+ controlLockTimeoutMs?: number;
1441
1563
  },
1442
1564
  ): Promise<SubmitHumanPromptResult> {
1443
1565
  const annotations = TimelineAnnotations.parse(input.annotations ?? []);
1444
- const workspaceControl = await lockWorkspaceInferenceControl(db, input.workspaceId, "update");
1566
+ // Send is inert queue admission while paused; Steer deliberately resumes.
1567
+ // Tell the lock helper which path may need the exclusive control prefix.
1568
+ const admission = await lockWorkspaceInferenceControlForAdmission(db, {
1569
+ workspaceId: input.workspaceId,
1570
+ sessionId: input.sessionId,
1571
+ resumePausedBranch: input.delivery === "steer",
1572
+ ...(input.controlLockTimeoutMs !== undefined
1573
+ ? { lockTimeoutMs: input.controlLockTimeoutMs }
1574
+ : {}),
1575
+ });
1576
+ const workspaceControl = admission.control;
1577
+ if (input.actor.type === "human") {
1578
+ await setSubjectRlsContext(db, input.actor.subjectId);
1579
+ await assertActiveManagedHumanOrganizationMembership(db, {
1580
+ accountId: input.accountId,
1581
+ subjectId: input.actor.subjectId,
1582
+ });
1583
+ }
1445
1584
  await lockSessionEventWriteRows(db, {
1446
1585
  workspaceId: input.workspaceId,
1447
1586
  controlLock: "already_locked",
@@ -1467,6 +1606,8 @@ export async function submitHumanPromptInTransaction(
1467
1606
  turnMetadata: input.turnMetadata ?? {},
1468
1607
  messagePresentation: input.messagePresentation ?? null,
1469
1608
  mcpCredentialUpdates: input.mcpCredentialUpdates ?? [],
1609
+ personalConnectionDelegations: input.personalConnectionDelegations ?? [],
1610
+ personalResourceAttachment: input.personalResourceAttachment ?? null,
1470
1611
  ...(input.actor.type === "service"
1471
1612
  ? {
1472
1613
  serviceInitiator: {
@@ -1524,12 +1665,32 @@ export async function submitHumanPromptInTransaction(
1524
1665
  if (!accepted || !replayTurn || events.length !== eventIds.length) {
1525
1666
  throw new SessionControlInvariantError("Replayed prompt rows are incomplete");
1526
1667
  }
1668
+ const replayDraft =
1669
+ input.expectedDraftRevision === null || input.expectedDraftRevision === undefined
1670
+ ? null
1671
+ : await getComposerDraftInTransaction(db, {
1672
+ workspaceId: input.workspaceId,
1673
+ sessionId: input.sessionId,
1674
+ subjectId: input.subjectId,
1675
+ });
1527
1676
  return {
1528
1677
  receipt: reserved.receipt,
1678
+ routing:
1679
+ reserved.receipt.result.routing === "accepted_for_execution" ||
1680
+ reserved.receipt.result.routing === "queued_for_execution" ||
1681
+ reserved.receipt.result.routing === "accepted_for_steering"
1682
+ ? reserved.receipt.result.routing
1683
+ : input.delivery === "steer"
1684
+ ? "accepted_for_steering"
1685
+ : // Pre-routing receipts cannot reconstruct admission-time activity
1686
+ // or Pause truth. Queue is the only conservative placement; a
1687
+ // later turn.started event moves it into chat without duplication.
1688
+ "queued_for_execution",
1529
1689
  queueVersion: Number(reserved.receipt.appliedQueueVersion),
1530
1690
  accepted,
1531
1691
  events,
1532
1692
  turn: mapSubmittedPromptTurn(replayTurn),
1693
+ draft: replayDraft,
1533
1694
  acceptedEventId,
1534
1695
  eventIds,
1535
1696
  turnId,
@@ -1550,34 +1711,39 @@ export async function submitHumanPromptInTransaction(
1550
1711
  throw new SessionControlConflictError();
1551
1712
  }
1552
1713
 
1553
- const resumed = await autoResumeSessionBranchInTransaction(db, {
1554
- workspaceId: input.workspaceId,
1555
- sessionId: input.sessionId,
1556
- actor:
1557
- input.actor.type === "agent_attempt"
1558
- ? `attempt:${input.actor.attemptId}`
1559
- : input.actor.subjectId,
1560
- reason:
1561
- input.actor.type === "service"
1562
- ? input.delivery === "steer"
1563
- ? "service_steer"
1564
- : "service_send"
1565
- : input.delivery === "steer"
1566
- ? "human_steer"
1567
- : "human_send",
1568
- observedControlEtag: input.controlEtag ?? null,
1569
- });
1570
1714
  const session = await lockSession(db, input.workspaceId, input.sessionId);
1571
1715
  if (session.status === "cancelled") {
1572
1716
  throw new QueueCommandConflictError(
1573
1717
  "QUEUE_PROMPT_STARTED",
1574
- "Cancelled session cannot accept work",
1575
- {
1576
- queueVersion: session.queueVersion,
1577
- },
1718
+ "Cancelled session subtree cannot accept work",
1719
+ { queueVersion: session.queueVersion },
1578
1720
  );
1579
1721
  }
1580
1722
 
1723
+ // Send is queue admission, never a hidden control mutation. A paused Send
1724
+ // remains inert behind the existing gate until the user explicitly resumes.
1725
+ // Steer is the intentional exception: it replaces the current direction and
1726
+ // therefore activates the selected branch atomically.
1727
+ const resumed =
1728
+ input.delivery === "send"
1729
+ ? {
1730
+ revision: before.controlVersion,
1731
+ control: before,
1732
+ changed: false,
1733
+ workspaceControlEventId: null,
1734
+ }
1735
+ : await autoResumeSessionBranchInTransaction(db, {
1736
+ workspaceId: input.workspaceId,
1737
+ sessionId: input.sessionId,
1738
+ actor:
1739
+ input.actor.type === "agent_attempt"
1740
+ ? `attempt:${input.actor.attemptId}`
1741
+ : input.actor.subjectId,
1742
+ reason: input.actor.type === "service" ? "service_steer" : "human_steer",
1743
+ observedControlEtag: input.controlEtag ?? null,
1744
+ admission,
1745
+ });
1746
+
1581
1747
  const draft =
1582
1748
  input.expectedDraftRevision === null || input.expectedDraftRevision === undefined
1583
1749
  ? null
@@ -1710,6 +1876,28 @@ export async function submitHumanPromptInTransaction(
1710
1876
  input.subjectLabel,
1711
1877
  );
1712
1878
  }
1879
+ const acceptedInitiatingHumanSubjectId = editedSourceTurn
1880
+ ? (editedSourceTurn.initiatingHumanSubjectId ??
1881
+ (editedSourceTurn.initiatorKind === "subject" ? editedSourceTurn.initiatorSubjectId : null))
1882
+ : (frozenInitiator.initiatingHumanSubjectId ??
1883
+ (frozenInitiator.initiator.kind === "subject" ? frozenInitiator.initiator.subjectId : null));
1884
+ if (
1885
+ (input.personalConnectionDelegations ?? []).some(
1886
+ (delegation) => delegation.userDelegation !== undefined,
1887
+ ) ||
1888
+ input.personalResourceAttachment !== undefined
1889
+ ) {
1890
+ if (!acceptedInitiatingHumanSubjectId) {
1891
+ throw new SessionControlInvariantError("Personal authority requires an exact causal human");
1892
+ }
1893
+ await db.execute(
1894
+ sql`select set_config(
1895
+ 'opengeni.initiating_human_subject_id',
1896
+ ${acceptedInitiatingHumanSubjectId},
1897
+ true
1898
+ )`,
1899
+ );
1900
+ }
1713
1901
  const xaiProviderAccountAuthoritySnapshot = editedSourceTurn
1714
1902
  ? XaiProviderAccountAuthoritySnapshotV1.parse(
1715
1903
  editedSourceTurn.xaiProviderAccountAuthoritySnapshot,
@@ -1726,6 +1914,13 @@ export async function submitHumanPromptInTransaction(
1726
1914
  editedSourceModelContext !== undefined
1727
1915
  ? editedSourceModelContext
1728
1916
  : (input.modelContext ?? null);
1917
+ const existingQueued = await loadQueuedTurns(db, input.workspaceId, input.sessionId, true);
1918
+ const routing: SessionPromptRouting =
1919
+ input.delivery === "steer"
1920
+ ? "accepted_for_steering"
1921
+ : before.state === "paused" || session.activeTurnId || existingQueued.length > 0
1922
+ ? "queued_for_execution"
1923
+ : "accepted_for_execution";
1729
1924
  let sequence = session.lastSequence;
1730
1925
  const eventValues: SessionEventInsertWithPayload[] = [
1731
1926
  {
@@ -1753,12 +1948,12 @@ export async function submitHumanPromptInTransaction(
1753
1948
  ...(input.reasoningEffort ? { reasoningEffort: input.reasoningEffort } : {}),
1754
1949
  ...(input.latencyMode ? { latencyMode: input.latencyMode } : {}),
1755
1950
  delivery: input.delivery,
1951
+ routing,
1756
1952
  initiator: frozenInitiator.initiator,
1757
1953
  },
1758
1954
  occurredAt: now,
1759
1955
  },
1760
1956
  ];
1761
- const existingQueued = await loadQueuedTurns(db, input.workspaceId, input.sessionId, true);
1762
1957
  const [turn] = await db
1763
1958
  .insert(schema.sessionTurns)
1764
1959
  .values(
@@ -1772,6 +1967,7 @@ export async function submitHumanPromptInTransaction(
1772
1967
  temporalWorkflowId: workflowId,
1773
1968
  status: "queued",
1774
1969
  source: input.source,
1970
+ promptRouting: routing,
1775
1971
  position: input.delivery === "steer" ? 0 : existingQueued.length + 1,
1776
1972
  prompt: input.text,
1777
1973
  annotations,
@@ -1788,14 +1984,7 @@ export async function submitHumanPromptInTransaction(
1788
1984
  : (input.turnMetadata ?? {}),
1789
1985
  lineage: { actor: input.actor.type },
1790
1986
  ...initiatorColumns(frozenInitiator),
1791
- initiatingHumanSubjectId: editedSourceTurn
1792
- ? (editedSourceTurn.initiatingHumanSubjectId ??
1793
- (editedSourceTurn.initiatorKind === "subject"
1794
- ? editedSourceTurn.initiatorSubjectId
1795
- : null))
1796
- : frozenInitiator.initiator.kind === "subject"
1797
- ? frozenInitiator.initiator.subjectId
1798
- : null,
1987
+ initiatingHumanSubjectId: acceptedInitiatingHumanSubjectId,
1799
1988
  personalConnectionDelegations: editedSourceTurn
1800
1989
  ? editedSourceTurn.personalConnectionDelegations
1801
1990
  : (input.personalConnectionDelegations ?? []),
@@ -1810,6 +1999,49 @@ export async function submitHumanPromptInTransaction(
1810
1999
  .returning();
1811
2000
  if (!turn) throw new SessionControlInvariantError("Prompt turn was not inserted");
1812
2001
  let committedTurn = turn;
2002
+ const personalResourceExpectedAuthorityEpoch =
2003
+ input.personalResourceAttachment?.expectedAuthorityEpoch;
2004
+ let acceptedPersonalResources: Awaited<
2005
+ ReturnType<typeof acceptTurnPersonalResourceAttachmentInTransaction>
2006
+ > | null = null;
2007
+ if (input.personalResourceAttachment) {
2008
+ if (personalResourceExpectedAuthorityEpoch === undefined) {
2009
+ throw new SessionControlInvariantError(
2010
+ "Personal-resource attachment requires an expected authority epoch",
2011
+ );
2012
+ }
2013
+ acceptedPersonalResources = await acceptTurnPersonalResourceAttachmentInTransaction(db, {
2014
+ accountId: input.accountId,
2015
+ workspaceId: input.workspaceId,
2016
+ sessionId: input.sessionId,
2017
+ turnId,
2018
+ subjectId: acceptedInitiatingHumanSubjectId!,
2019
+ intent: {
2020
+ ...input.personalResourceAttachment,
2021
+ expectedAuthorityEpoch: personalResourceExpectedAuthorityEpoch,
2022
+ },
2023
+ });
2024
+ }
2025
+ if (acceptedPersonalResources) {
2026
+ committedTurn = {
2027
+ ...turn,
2028
+ personalResourceAttachmentSummary: acceptedPersonalResources.summary,
2029
+ personalResourceProtocolVersion: 1,
2030
+ };
2031
+ eventValues.push({
2032
+ accountId: input.accountId,
2033
+ workspaceId: input.workspaceId,
2034
+ sessionId: input.sessionId,
2035
+ turnId,
2036
+ sequence: ++sequence,
2037
+ type: "session.personal_resources.attached",
2038
+ payload: {
2039
+ turnId,
2040
+ ...acceptedPersonalResources.summary,
2041
+ },
2042
+ occurredAt: now,
2043
+ });
2044
+ }
1813
2045
  eventValues.push({
1814
2046
  accountId: input.accountId,
1815
2047
  workspaceId: input.workspaceId,
@@ -1821,6 +2053,7 @@ export async function submitHumanPromptInTransaction(
1821
2053
  turnId,
1822
2054
  triggerEventId: acceptedEventId,
1823
2055
  source: input.source,
2056
+ routing,
1824
2057
  initiator: frozenInitiator.initiator,
1825
2058
  },
1826
2059
  occurredAt: now,
@@ -1919,6 +2152,29 @@ export async function submitHumanPromptInTransaction(
1919
2152
  });
1920
2153
  }
1921
2154
 
2155
+ // A human/API prompt is external input for the goal. A goal paused only by
2156
+ // its continuation ceiling (`max_auto_continuations`, pacing rather than
2157
+ // intent) resumes in this same commit; a user/API/agent/limits pause is never
2158
+ // touched here. Goal row FOR UPDATE follows the session and turn locks above,
2159
+ // the order the claim transaction uses.
2160
+ const goalAutoResumed = await autoResumeGoalPausedByCapInTransaction(db, {
2161
+ workspaceId: input.workspaceId,
2162
+ sessionId: input.sessionId,
2163
+ cause: { kind: "human_prompt", turnId },
2164
+ now,
2165
+ });
2166
+ if (goalAutoResumed) {
2167
+ eventValues.push({
2168
+ accountId: input.accountId,
2169
+ workspaceId: input.workspaceId,
2170
+ sessionId: input.sessionId,
2171
+ sequence: ++sequence,
2172
+ type: "goal.resumed",
2173
+ payload: goalAutoResumed.payload,
2174
+ occurredAt: now,
2175
+ });
2176
+ }
2177
+
1922
2178
  const ordered =
1923
2179
  input.delivery === "steer" ? [turn, ...existingQueued] : [...existingQueued, turn];
1924
2180
  await normalizeQueuePositions(
@@ -1946,12 +2202,6 @@ export async function submitHumanPromptInTransaction(
1946
2202
  .values(withLosslessContentWriteVersion(eventValues, "payload", "payloadCodecVersion"))
1947
2203
  .returning();
1948
2204
  if (input.actor.type === "human") {
1949
- const realtimeRouting =
1950
- input.delivery === "steer"
1951
- ? "accepted_for_steering"
1952
- : session.activeTurnId || existingQueued.length > 0
1953
- ? "queued_for_execution"
1954
- : "accepted_for_execution";
1955
2205
  await mirrorSessionRealtimeContextInTransaction(db, {
1956
2206
  accountId: input.accountId,
1957
2207
  workspaceId: input.workspaceId,
@@ -1962,12 +2212,12 @@ export async function submitHumanPromptInTransaction(
1962
2212
  channel: null,
1963
2213
  text: renderRealtimeHumanInputContext({
1964
2214
  delivery: input.delivery,
1965
- routing: realtimeRouting,
2215
+ routing,
1966
2216
  text: renderTimelineAnnotationsForModel(input.text, annotations),
1967
2217
  }),
1968
2218
  payload: {
1969
2219
  delivery: input.delivery,
1970
- routing: realtimeRouting,
2220
+ routing,
1971
2221
  acceptedEventId,
1972
2222
  instruction: "OpenGeni accepted and routed this user input; do not delegate it again.",
1973
2223
  },
@@ -1989,8 +2239,25 @@ export async function submitHumanPromptInTransaction(
1989
2239
  updatedAt: now,
1990
2240
  })
1991
2241
  .where(eq(schema.sessions.id, input.sessionId));
1992
- if (draft) {
1993
- await db.delete(schema.composerDrafts).where(eq(schema.composerDrafts.id, draft.id));
2242
+ const [nextDraft] = draft
2243
+ ? await db
2244
+ .update(schema.composerDrafts)
2245
+ .set({
2246
+ revision: draft.revision + 1,
2247
+ text: "",
2248
+ annotations: [],
2249
+ resources: [],
2250
+ tools: [],
2251
+ toolsProvided: false,
2252
+ sourceTurnId: null,
2253
+ sourceTurnVersion: null,
2254
+ updatedAt: now,
2255
+ })
2256
+ .where(eq(schema.composerDrafts.id, draft.id))
2257
+ .returning()
2258
+ : [undefined];
2259
+ if (draft && !nextDraft) {
2260
+ throw new SessionControlInvariantError("Accepted composer draft did not rotate");
1994
2261
  }
1995
2262
  const wakeRevision = await registerSessionWorkflowWakeInTransaction(db, {
1996
2263
  accountId: input.accountId,
@@ -2051,7 +2318,7 @@ export async function submitHumanPromptInTransaction(
2051
2318
  controlRevision: resumed.revision,
2052
2319
  queueVersion,
2053
2320
  turnVersion: turn.version,
2054
- ...(draft ? { draftRevision: draft.revision } : {}),
2321
+ ...(nextDraft ? { draftRevision: nextDraft.revision } : {}),
2055
2322
  result: {
2056
2323
  turnId,
2057
2324
  acceptedEventId,
@@ -2060,6 +2327,7 @@ export async function submitHumanPromptInTransaction(
2060
2327
  interruptionCount,
2061
2328
  replacedTurnId,
2062
2329
  workspaceControlEventId: resumed.workspaceControlEventId,
2330
+ routing,
2063
2331
  ...(input.turnExecutionPolicy
2064
2332
  ? {
2065
2333
  executionPolicy: turnExecutionPolicyAuditMetadata(input.turnExecutionPolicy, turnId),
@@ -2074,10 +2342,12 @@ export async function submitHumanPromptInTransaction(
2074
2342
  }
2075
2343
  return {
2076
2344
  receipt,
2345
+ routing,
2077
2346
  queueVersion,
2078
2347
  accepted,
2079
2348
  events,
2080
2349
  turn: mapSubmittedPromptTurn(committedTurn),
2350
+ draft: nextDraft ?? null,
2081
2351
  acceptedEventId,
2082
2352
  eventIds,
2083
2353
  turnId,
@@ -2097,9 +2367,18 @@ export async function sendAgentMessageInTransaction(
2097
2367
  actor: Extract<SessionCommandActor, { type: "agent_attempt" }>;
2098
2368
  operationKey: string;
2099
2369
  text: string;
2370
+ /**
2371
+ * Bound the workspace control prefix wait (request-scoped API callers pass
2372
+ * `workspaceControlRequestLockTimeoutMs()`); omit for lifecycle callers.
2373
+ */
2374
+ controlLockTimeoutMs?: number;
2100
2375
  },
2101
2376
  ): Promise<AgentInternalUpdateCommandResult> {
2102
- const workspaceControl = await lockWorkspaceInferenceControl(db, input.workspaceId, "share");
2377
+ const workspaceControl = await lockWorkspaceInferenceControl(db, input.workspaceId, "share", {
2378
+ ...(input.controlLockTimeoutMs !== undefined
2379
+ ? { lockTimeoutMs: input.controlLockTimeoutMs }
2380
+ : {}),
2381
+ });
2103
2382
  await lockSessionEventWriteRows(db, {
2104
2383
  workspaceId: input.workspaceId,
2105
2384
  controlLock: "already_locked",
@@ -2148,11 +2427,13 @@ export async function sendAgentMessageInTransaction(
2148
2427
  targetSessionId: input.targetSessionId,
2149
2428
  action: "message",
2150
2429
  });
2151
- const personalConnectionDelegations = await personalConnectionDelegationsForAgentActor(
2430
+ const inheritedConnectionAuthority = await personalConnectionDelegationsForAgentActor(
2152
2431
  db,
2153
2432
  input.workspaceId,
2154
2433
  input.actor,
2434
+ input.targetSessionId,
2155
2435
  );
2436
+ const personalConnectionDelegations = inheritedConnectionAuthority.delegations;
2156
2437
  const xaiAuthority = await xaiAuthorityForAgentActor(db, input.workspaceId, input.actor);
2157
2438
  const session = await lockSession(db, input.workspaceId, input.targetSessionId);
2158
2439
  if (session.status === "cancelled") {
@@ -2195,6 +2476,12 @@ export async function sendAgentMessageInTransaction(
2195
2476
  callerTurnId: input.actor.turnId,
2196
2477
  callerAttemptId: input.actor.attemptId,
2197
2478
  callerExecutionGeneration: input.actor.executionGeneration,
2479
+ ...(inheritedConnectionAuthority.connectionAuthoritySubjectId
2480
+ ? {
2481
+ connectionAuthoritySubjectId:
2482
+ inheritedConnectionAuthority.connectionAuthoritySubjectId,
2483
+ }
2484
+ : {}),
2198
2485
  ...(xaiAuthority.subjectId ? { xaiAuthoritySubjectId: xaiAuthority.subjectId } : {}),
2199
2486
  },
2200
2487
  personalConnectionDelegations,
@@ -2210,29 +2497,52 @@ export async function sendAgentMessageInTransaction(
2210
2497
  )
2211
2498
  .returning({ id: schema.sessionSystemUpdates.id });
2212
2499
  if (!update) throw new SessionControlInvariantError("Agent message was not inserted");
2213
- const [event] = await db
2214
- .insert(schema.sessionEvents)
2215
- .values(
2216
- withLosslessContentWriteVersion(
2217
- {
2218
- accountId: input.accountId,
2219
- workspaceId: input.workspaceId,
2220
- sessionId: input.targetSessionId,
2221
- sequence: session.lastSequence + 1,
2222
- type: "system.update.pending",
2223
- payload: {
2224
- updateId: update.id,
2225
- kind: "agent_message",
2226
- sourceSessionId: input.actor.sessionId,
2500
+ // An Agent message is external input for the target's goal. A goal paused
2501
+ // only by its continuation ceiling (`max_auto_continuations`, pacing rather
2502
+ // than intent) resumes in this same commit; any other pause stays. Goal row
2503
+ // FOR UPDATE follows the session lock above, the goal tools' order.
2504
+ const goalAutoResumed = await autoResumeGoalPausedByCapInTransaction(db, {
2505
+ workspaceId: input.workspaceId,
2506
+ sessionId: input.targetSessionId,
2507
+ cause: { kind: "agent_message", updateId: update.id },
2508
+ now,
2509
+ });
2510
+ const eventValues: SessionEventInsertWithPayload[] = [
2511
+ {
2512
+ accountId: input.accountId,
2513
+ workspaceId: input.workspaceId,
2514
+ sessionId: input.targetSessionId,
2515
+ sequence: session.lastSequence + 1,
2516
+ type: "system.update.pending",
2517
+ payload: {
2518
+ updateId: update.id,
2519
+ kind: "agent_message",
2520
+ sourceSessionId: input.actor.sessionId,
2521
+ },
2522
+ occurredAt: now,
2523
+ },
2524
+ ...(goalAutoResumed
2525
+ ? [
2526
+ {
2527
+ accountId: input.accountId,
2528
+ workspaceId: input.workspaceId,
2529
+ sessionId: input.targetSessionId,
2530
+ sequence: session.lastSequence + 2,
2531
+ type: "goal.resumed" as const,
2532
+ payload: goalAutoResumed.payload,
2533
+ occurredAt: now,
2227
2534
  },
2228
- occurredAt: now,
2229
- },
2230
- "payload",
2231
- "payloadCodecVersion",
2232
- ),
2233
- )
2535
+ ]
2536
+ : []),
2537
+ ];
2538
+ const insertedEvents = await db
2539
+ .insert(schema.sessionEvents)
2540
+ .values(withLosslessContentWriteVersion(eventValues, "payload", "payloadCodecVersion"))
2234
2541
  .returning({ id: schema.sessionEvents.id });
2235
- if (!event) throw new SessionControlInvariantError("Agent message event was not inserted");
2542
+ if (insertedEvents.length !== eventValues.length) {
2543
+ throw new SessionControlInvariantError("Agent message event was not inserted");
2544
+ }
2545
+ const eventIds = insertedEvents.map((event) => event.id);
2236
2546
  const workflowId = session.temporalWorkflowId ?? `session-${session.id}`;
2237
2547
  const runnable = !realtimeActive && session.activeTurnId === null && effective.state === "active";
2238
2548
  const wake = runnable
@@ -2246,7 +2556,7 @@ export async function sendAgentMessageInTransaction(
2246
2556
  await db
2247
2557
  .update(schema.sessions)
2248
2558
  .set({
2249
- lastSequence: session.lastSequence + 1,
2559
+ lastSequence: session.lastSequence + eventValues.length,
2250
2560
  ...(runnable ? { status: "queued" as const } : {}),
2251
2561
  updatedAt: now,
2252
2562
  })
@@ -2275,7 +2585,7 @@ export async function sendAgentMessageInTransaction(
2275
2585
  const receipt = await updateSessionCommandReceiptResult(db, reserved.receipt.id, {
2276
2586
  result: {
2277
2587
  updateId: update.id,
2278
- eventIds: [event.id],
2588
+ eventIds,
2279
2589
  wakeRevision: wake?.wakeRevision ?? null,
2280
2590
  workflowId,
2281
2591
  effectiveState: effective.state,
@@ -2284,7 +2594,7 @@ export async function sendAgentMessageInTransaction(
2284
2594
  return {
2285
2595
  receipt,
2286
2596
  updateId: update.id,
2287
- eventIds: [event.id],
2597
+ eventIds,
2288
2598
  wakeRevision: wake?.wakeRevision ?? null,
2289
2599
  shouldSignal: wake?.shouldSignal ?? false,
2290
2600
  workflowId,
@@ -2304,9 +2614,18 @@ export async function steerAgentSessionInTransaction(
2304
2614
  actor: Extract<SessionCommandActor, { type: "agent_attempt" }>;
2305
2615
  operationKey: string;
2306
2616
  instruction: string;
2617
+ /** Request-scoped callers bound the control prefix wait; lifecycle callers omit it. */
2618
+ controlLockTimeoutMs?: number;
2307
2619
  },
2308
2620
  ): Promise<AgentInternalUpdateCommandResult> {
2309
- await lockWorkspaceInferenceControl(db, input.workspaceId, "update");
2621
+ const admission = await lockWorkspaceInferenceControlForAdmission(db, {
2622
+ workspaceId: input.workspaceId,
2623
+ sessionId: input.targetSessionId,
2624
+ resumePausedBranch: true,
2625
+ ...(input.controlLockTimeoutMs !== undefined
2626
+ ? { lockTimeoutMs: input.controlLockTimeoutMs }
2627
+ : {}),
2628
+ });
2310
2629
  await lockSessionEventWriteRows(db, {
2311
2630
  workspaceId: input.workspaceId,
2312
2631
  controlLock: "already_locked",
@@ -2358,17 +2677,20 @@ export async function steerAgentSessionInTransaction(
2358
2677
  targetSessionId: input.targetSessionId,
2359
2678
  action: "steer",
2360
2679
  });
2361
- const personalConnectionDelegations = await personalConnectionDelegationsForAgentActor(
2680
+ const inheritedConnectionAuthority = await personalConnectionDelegationsForAgentActor(
2362
2681
  db,
2363
2682
  input.workspaceId,
2364
2683
  input.actor,
2684
+ input.targetSessionId,
2365
2685
  );
2686
+ const personalConnectionDelegations = inheritedConnectionAuthority.delegations;
2366
2687
  const xaiAuthority = await xaiAuthorityForAgentActor(db, input.workspaceId, input.actor);
2367
2688
  const resumed = await autoResumeSessionBranchInTransaction(db, {
2368
2689
  workspaceId: input.workspaceId,
2369
2690
  sessionId: input.targetSessionId,
2370
2691
  actor: `attempt:${input.actor.attemptId}`,
2371
2692
  reason: "agent_steer",
2693
+ admission,
2372
2694
  });
2373
2695
  const session = await lockSession(db, input.workspaceId, input.targetSessionId);
2374
2696
  if (session.status === "cancelled") {
@@ -2378,6 +2700,20 @@ export async function steerAgentSessionInTransaction(
2378
2700
  { queueVersion: session.queueVersion },
2379
2701
  );
2380
2702
  }
2703
+ const now = new Date();
2704
+ const updateId = crypto.randomUUID();
2705
+ // An Agent Steer is external input for the target's goal. A goal paused only
2706
+ // by its continuation ceiling (`max_auto_continuations`, pacing rather than
2707
+ // intent) resumes here, before the supersession, so the Steer starts a fresh
2708
+ // continuation epoch; any other pause stays. Goal row FOR UPDATE follows the
2709
+ // session lock above; the `goal.resumed` fact is appended below at the next
2710
+ // sequence of this same commit.
2711
+ const goalAutoResumed = await autoResumeGoalPausedByCapInTransaction(db, {
2712
+ workspaceId: input.workspaceId,
2713
+ sessionId: input.targetSessionId,
2714
+ cause: { kind: "agent_steer_instruction", updateId },
2715
+ now,
2716
+ });
2381
2717
  const supersession = await supersedeSessionCurrentDirectionInTransaction(db, {
2382
2718
  accountId: input.accountId,
2383
2719
  workspaceId: input.workspaceId,
@@ -2400,13 +2736,13 @@ export async function steerAgentSessionInTransaction(
2400
2736
  ),
2401
2737
  )
2402
2738
  .returning({ id: schema.sessionSystemUpdates.id });
2403
- const now = new Date();
2404
2739
  const [update] = await db
2405
2740
  .insert(schema.sessionSystemUpdates)
2406
2741
  .values(
2407
2742
  withLosslessContentWriteVersion(
2408
2743
  withLosslessContentWriteVersion(
2409
2744
  {
2745
+ id: updateId,
2410
2746
  accountId: input.accountId,
2411
2747
  workspaceId: input.workspaceId,
2412
2748
  sessionId: input.targetSessionId,
@@ -2425,6 +2761,12 @@ export async function steerAgentSessionInTransaction(
2425
2761
  callerTurnId: input.actor.turnId,
2426
2762
  callerAttemptId: input.actor.attemptId,
2427
2763
  callerExecutionGeneration: input.actor.executionGeneration,
2764
+ ...(inheritedConnectionAuthority.connectionAuthoritySubjectId
2765
+ ? {
2766
+ connectionAuthoritySubjectId:
2767
+ inheritedConnectionAuthority.connectionAuthoritySubjectId,
2768
+ }
2769
+ : {}),
2428
2770
  ...(xaiAuthority.subjectId ? { xaiAuthoritySubjectId: xaiAuthority.subjectId } : {}),
2429
2771
  },
2430
2772
  personalConnectionDelegations,
@@ -2506,6 +2848,17 @@ export async function steerAgentSessionInTransaction(
2506
2848
  occurredAt: now,
2507
2849
  },
2508
2850
  );
2851
+ if (goalAutoResumed) {
2852
+ events.push({
2853
+ accountId: input.accountId,
2854
+ workspaceId: input.workspaceId,
2855
+ sessionId: input.targetSessionId,
2856
+ sequence: ++sequence,
2857
+ type: "goal.resumed",
2858
+ payload: goalAutoResumed.payload,
2859
+ occurredAt: now,
2860
+ });
2861
+ }
2509
2862
  const insertedEvents = await db
2510
2863
  .insert(schema.sessionEvents)
2511
2864
  .values(withLosslessContentWriteVersion(events, "payload", "payloadCodecVersion"))