@opengeni/core 1.0.1 → 2.2.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/access/index.d.ts +37 -0
  2. package/dist/application/new-session-drafts.d.ts +8 -1
  3. package/dist/application/session-commands.d.ts +18 -11
  4. package/dist/application/session-tenancy.d.ts +18 -0
  5. package/dist/application/user-resource-grants.d.ts +20 -0
  6. package/dist/{chunk-6WKPWE5S.js → chunk-H7X52RI2.js} +27 -18
  7. package/dist/chunk-H7X52RI2.js.map +1 -0
  8. package/dist/{chunk-JJU6I5XD.js → chunk-VCZFFEPB.js} +21 -9
  9. package/dist/chunk-VCZFFEPB.js.map +1 -0
  10. package/dist/{chunk-NYPMQ7NO.js → chunk-ZYCNLYGH.js} +29 -27
  11. package/dist/chunk-ZYCNLYGH.js.map +1 -0
  12. package/dist/dependencies.d.ts +12 -1
  13. package/dist/domain/automations.d.ts +51 -0
  14. package/dist/domain/company-brain-governed-writes.d.ts +70 -0
  15. package/dist/domain/company-profile-agent-admin.d.ts +24 -0
  16. package/dist/domain/connection-authority.d.ts +97 -0
  17. package/dist/domain/connection-authority.js +169 -0
  18. package/dist/domain/connection-authority.js.map +1 -0
  19. package/dist/domain/editable-artifacts/service.d.ts +1 -2
  20. package/dist/domain/environments.d.ts +1 -1
  21. package/dist/domain/github-repository-bindings.d.ts +117 -0
  22. package/dist/domain/governed-learning-activation.d.ts +16 -0
  23. package/dist/domain/governed-learning-evaluator.d.ts +6 -0
  24. package/dist/domain/governed-learning-slack-publication.d.ts +45 -0
  25. package/dist/domain/memory-slack-delivery.d.ts +1 -4
  26. package/dist/domain/organization-membership-lifecycle.d.ts +1 -0
  27. package/dist/domain/packs.d.ts +5 -1
  28. package/dist/domain/personal-connection-delegations.d.ts +34 -2
  29. package/dist/domain/pr-review.d.ts +149 -0
  30. package/dist/domain/remember.d.ts +69 -0
  31. package/dist/domain/resources.d.ts +22 -1
  32. package/dist/domain/scheduled-tasks.d.ts +43 -2
  33. package/dist/domain/session-tool-policy.d.ts +1 -1
  34. package/dist/domain/sessions.d.ts +40 -6
  35. package/dist/editable-artifact-live/application.d.ts +10 -2
  36. package/dist/editable-artifact-live/ports.d.ts +0 -1
  37. package/dist/editable-artifact-live/server.d.ts +0 -1
  38. package/dist/editable-artifact-live/ticket.d.ts +0 -1
  39. package/dist/editable-artifact-live/types.d.ts +5 -2
  40. package/dist/editable-artifact-live.js +2 -2
  41. package/dist/editable-artifacts.js +2 -4
  42. package/dist/index.d.ts +13 -1
  43. package/dist/index.js +4827 -1164
  44. package/dist/index.js.map +1 -1
  45. package/dist/rigs/index.d.ts +22 -5
  46. package/dist/sandbox/fleet.d.ts +34 -6
  47. package/dist/sandbox/routing.d.ts +15 -1
  48. package/dist/sandbox/runtime-settings.d.ts +39 -0
  49. package/package.json +16 -11
  50. package/src/access/index.ts +157 -31
  51. package/src/application/new-session-drafts.ts +24 -2
  52. package/src/application/session-commands.ts +305 -71
  53. package/src/application/session-tenancy.ts +321 -0
  54. package/src/application/user-resource-grants.ts +140 -0
  55. package/src/billing/limits.ts +25 -15
  56. package/src/dependencies.ts +12 -1
  57. package/src/domain/automations.ts +196 -0
  58. package/src/domain/capabilities.ts +7 -1
  59. package/src/domain/company-brain-governed-writes.ts +330 -0
  60. package/src/domain/company-profile-agent-admin.ts +55 -0
  61. package/src/domain/connection-authority.ts +329 -0
  62. package/src/domain/editable-artifacts/agent-application.ts +17 -9
  63. package/src/domain/editable-artifacts/service.ts +12 -1
  64. package/src/domain/editable-artifacts/snapshot-compaction.ts +9 -5
  65. package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +10 -8
  66. package/src/domain/editable-artifacts/snapshot-verifier.ts +10 -3
  67. package/src/domain/environments.ts +16 -3
  68. package/src/domain/github-repository-bindings.ts +295 -0
  69. package/src/domain/governed-learning-activation.ts +44 -0
  70. package/src/domain/governed-learning-evaluator.ts +49 -0
  71. package/src/domain/governed-learning-slack-publication.ts +157 -0
  72. package/src/domain/insights.ts +9 -0
  73. package/src/domain/memory-slack-delivery.ts +0 -30
  74. package/src/domain/organization-membership-lifecycle.ts +9 -0
  75. package/src/domain/packs.ts +106 -3
  76. package/src/domain/personal-connection-delegations.ts +532 -51
  77. package/src/domain/pr-review.ts +671 -0
  78. package/src/domain/remember.ts +549 -0
  79. package/src/domain/resources.ts +131 -4
  80. package/src/domain/scheduled-tasks.ts +497 -76
  81. package/src/domain/session-tool-policy.ts +12 -9
  82. package/src/domain/sessions.ts +613 -166
  83. package/src/editable-artifact-live/application.ts +21 -3
  84. package/src/editable-artifact-live/ports.ts +0 -1
  85. package/src/editable-artifact-live/server.ts +13 -7
  86. package/src/editable-artifact-live/ticket.ts +1 -5
  87. package/src/editable-artifact-live/types.ts +6 -2
  88. package/src/editable-artifact-live/wire.ts +0 -10
  89. package/src/index.ts +13 -1
  90. package/src/rigs/index.ts +165 -48
  91. package/src/sandbox/fleet.ts +376 -30
  92. package/src/sandbox/routing.ts +91 -29
  93. package/src/sandbox/runtime-settings.ts +274 -0
  94. package/src/session-authorization.ts +17 -49
  95. package/dist/chunk-6WKPWE5S.js.map +0 -1
  96. package/dist/chunk-JJU6I5XD.js.map +0 -1
  97. package/dist/chunk-NYPMQ7NO.js.map +0 -1
  98. package/dist/domain/durable-learning-slack-publication.d.ts +0 -72
  99. package/src/domain/durable-learning-slack-publication.ts +0 -202
@@ -16,11 +16,7 @@ import type {
16
16
  WorkspaceInferenceControlRequest,
17
17
  WorkspaceInferenceControlResponse,
18
18
  } from "@opengeni/contracts";
19
- import {
20
- DraftTimelineAnnotations,
21
- latencyModeForMetadata,
22
- reasoningEffortForMetadata,
23
- } from "@opengeni/contracts";
19
+ import { DraftTimelineAnnotations } from "@opengeni/contracts";
24
20
  import {
25
21
  deleteSessionQueueItemInTransaction,
26
22
  editQueuedTurnInTransaction,
@@ -39,6 +35,7 @@ import {
39
35
  serializeEffectiveSessionControl,
40
36
  steerAgentSessionInTransaction,
41
37
  steerQueuedTurnInTransaction,
38
+ workspaceControlRequestLockTimeoutMs,
42
39
  withWorkspaceRls,
43
40
  withWorkspaceSessionActivityRls,
44
41
  withWorkspaceSubjectRls,
@@ -185,18 +182,68 @@ async function runSessionCommandPersistenceTransaction<T>(
185
182
  );
186
183
  }
187
184
 
188
- async function publishAndWakeAgentCommand(
185
+ type SessionCommandPostCommitDeps = {
186
+ /** Historical name; now schedules replayable follow-up for every interactive session command. */
187
+ schedulePromptPostCommit?: ((task: () => Promise<void>) => void) | undefined;
188
+ };
189
+
190
+ type SessionCommandPostCommitStep = {
191
+ kind: "session_event_fanout" | "workspace_control_fanout" | "workflow_wake";
192
+ run: () => Promise<void>;
193
+ };
194
+
195
+ /**
196
+ * A committed interactive command must never wait for ephemeral fanout or an
197
+ * immediate Temporal nudge. Durable events and wake outboxes are the recovery
198
+ * contract; these steps only reduce propagation latency.
199
+ */
200
+ function scheduleSessionCommandPostCommit(
201
+ deps: SessionCommandPostCommitDeps,
202
+ operation: string,
203
+ steps: SessionCommandPostCommitStep[],
204
+ ): void {
205
+ const task = async () => {
206
+ await Promise.all(
207
+ steps.map(async (step) => {
208
+ try {
209
+ await step.run();
210
+ } catch {
211
+ console.warn("[session-commands] replayable post-commit step failed", {
212
+ errorClass: "SessionCommandPostCommitError",
213
+ errorCode: "session_command_post_commit_failed",
214
+ origin: "core",
215
+ operation,
216
+ step: step.kind,
217
+ });
218
+ }
219
+ }),
220
+ );
221
+ };
222
+ try {
223
+ const schedule =
224
+ deps.schedulePromptPostCommit ??
225
+ ((pending: () => Promise<void>) => {
226
+ void pending();
227
+ });
228
+ schedule(task);
229
+ } catch {
230
+ console.warn("[session-commands] post-commit scheduling failed", {
231
+ errorClass: "SessionCommandPostCommitScheduleError",
232
+ errorCode: "session_command_post_commit_schedule_failed",
233
+ origin: "core",
234
+ operation,
235
+ });
236
+ }
237
+ }
238
+
239
+ async function wakeSessionCommand(
189
240
  deps: {
190
- db: Database;
191
- bus: EventBus;
192
241
  workflowClient: Pick<SessionWorkflowClient, "wakeSessionWorkflow">;
193
- sessionAuthorization?: SessionAuthorizationPort | null;
194
242
  },
195
243
  input: {
196
244
  accountId: string;
197
245
  workspaceId: string;
198
246
  sessionId: string;
199
- eventIds: string[];
200
247
  workflowId: string;
201
248
  wakeRevision: number | null;
202
249
  shouldSignal: boolean;
@@ -204,7 +251,6 @@ async function publishAndWakeAgentCommand(
204
251
  controlRequested?: boolean;
205
252
  },
206
253
  ): Promise<void> {
207
- await publishSessionEventIds(deps, input.workspaceId, input.sessionId, input.eventIds);
208
254
  if (!input.shouldSignal || input.wakeRevision === null) return;
209
255
  try {
210
256
  await deps.workflowClient.wakeSessionWorkflow({
@@ -218,14 +264,11 @@ async function publishAndWakeAgentCommand(
218
264
  : {}),
219
265
  });
220
266
  } catch {
221
- console.warn(
222
- "[session-commands] immediate Agent command wake failed; durable outbox will retry",
223
- {
224
- errorClass: "WorkflowWakeOperationError",
225
- errorCode: "agent_command_wake_failed",
226
- origin: "core",
227
- },
228
- );
267
+ console.warn("[session-commands] immediate command wake failed; durable outbox will retry", {
268
+ errorClass: "WorkflowWakeOperationError",
269
+ errorCode: "session_command_wake_failed",
270
+ origin: "core",
271
+ });
229
272
  }
230
273
  }
231
274
 
@@ -293,7 +336,7 @@ export async function sendAgentSessionMessage(
293
336
  bus: EventBus;
294
337
  workflowClient: Pick<SessionWorkflowClient, "wakeSessionWorkflow">;
295
338
  sessionAuthorization?: SessionAuthorizationPort | null;
296
- },
339
+ } & SessionCommandPostCommitDeps,
297
340
  context: AgentSessionCommandContext,
298
341
  input: { targetSessionId: string; text: string; idempotencyKey: string },
299
342
  ) {
@@ -312,20 +355,44 @@ export async function sendAgentSessionMessage(
312
355
  actor: agentActor(context),
313
356
  operationKey: input.idempotencyKey,
314
357
  text: input.text,
358
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
315
359
  }),
316
360
  },
317
361
  );
318
- await publishAndWakeAgentCommand(deps, {
319
- accountId: context.accountId,
320
- workspaceId: context.workspaceId,
321
- sessionId: input.targetSessionId,
322
- eventIds: result.eventIds,
323
- workflowId: result.workflowId,
324
- wakeRevision: result.wakeRevision,
325
- shouldSignal: result.shouldSignal,
326
- interruptionCount: 0,
327
- });
328
- await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
362
+ scheduleSessionCommandPostCommit(deps, "agent_message", [
363
+ {
364
+ kind: "session_event_fanout",
365
+ run: async () =>
366
+ await publishSessionEventIds(
367
+ deps,
368
+ context.workspaceId,
369
+ input.targetSessionId,
370
+ result.eventIds,
371
+ ),
372
+ },
373
+ {
374
+ kind: "workspace_control_fanout",
375
+ run: async () =>
376
+ await publishWorkspaceControlEvent(
377
+ deps,
378
+ context.workspaceId,
379
+ result.workspaceControlEventId,
380
+ ),
381
+ },
382
+ {
383
+ kind: "workflow_wake",
384
+ run: async () =>
385
+ await wakeSessionCommand(deps, {
386
+ accountId: context.accountId,
387
+ workspaceId: context.workspaceId,
388
+ sessionId: input.targetSessionId,
389
+ workflowId: result.workflowId,
390
+ wakeRevision: result.wakeRevision,
391
+ shouldSignal: result.shouldSignal,
392
+ interruptionCount: 0,
393
+ }),
394
+ },
395
+ ]);
329
396
  return result;
330
397
  }
331
398
 
@@ -335,7 +402,7 @@ export async function steerAgentSession(
335
402
  bus: EventBus;
336
403
  workflowClient: Pick<SessionWorkflowClient, "wakeSessionWorkflow">;
337
404
  sessionAuthorization?: SessionAuthorizationPort | null;
338
- },
405
+ } & SessionCommandPostCommitDeps,
339
406
  context: AgentSessionCommandContext,
340
407
  input: {
341
408
  targetSessionId: string;
@@ -358,21 +425,45 @@ export async function steerAgentSession(
358
425
  actor: agentActor(context),
359
426
  operationKey: input.idempotencyKey,
360
427
  instruction: input.instruction,
428
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
361
429
  }),
362
430
  },
363
431
  );
364
- await publishAndWakeAgentCommand(deps, {
365
- accountId: context.accountId,
366
- workspaceId: context.workspaceId,
367
- sessionId: input.targetSessionId,
368
- eventIds: result.eventIds,
369
- workflowId: result.workflowId,
370
- wakeRevision: result.wakeRevision,
371
- shouldSignal: result.shouldSignal,
372
- interruptionCount: result.interruptionCount,
373
- controlRequested: true,
374
- });
375
- await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
432
+ scheduleSessionCommandPostCommit(deps, "agent_steer", [
433
+ {
434
+ kind: "session_event_fanout",
435
+ run: async () =>
436
+ await publishSessionEventIds(
437
+ deps,
438
+ context.workspaceId,
439
+ input.targetSessionId,
440
+ result.eventIds,
441
+ ),
442
+ },
443
+ {
444
+ kind: "workspace_control_fanout",
445
+ run: async () =>
446
+ await publishWorkspaceControlEvent(
447
+ deps,
448
+ context.workspaceId,
449
+ result.workspaceControlEventId,
450
+ ),
451
+ },
452
+ {
453
+ kind: "workflow_wake",
454
+ run: async () =>
455
+ await wakeSessionCommand(deps, {
456
+ accountId: context.accountId,
457
+ workspaceId: context.workspaceId,
458
+ sessionId: input.targetSessionId,
459
+ workflowId: result.workflowId,
460
+ wakeRevision: result.wakeRevision,
461
+ shouldSignal: result.shouldSignal,
462
+ interruptionCount: result.interruptionCount,
463
+ controlRequested: true,
464
+ }),
465
+ },
466
+ ]);
376
467
  return result;
377
468
  }
378
469
 
@@ -380,9 +471,12 @@ export async function controlAgentSessionWorkstream(
380
471
  deps: {
381
472
  db: Database;
382
473
  bus: EventBus;
383
- workflowClient: Pick<SessionWorkflowClient, "requestSessionWorkflowWakeDispatch">;
474
+ workflowClient: Pick<
475
+ SessionWorkflowClient,
476
+ "requestSessionWorkflowWakeDispatch" | "wakeSessionWorkflow"
477
+ >;
384
478
  sessionAuthorization?: SessionAuthorizationPort | null;
385
- },
479
+ } & SessionCommandPostCommitDeps,
386
480
  context: AgentSessionCommandContext,
387
481
  input: {
388
482
  targetSessionId: string;
@@ -412,14 +506,48 @@ export async function controlAgentSessionWorkstream(
412
506
  operationKey: input.idempotencyKey,
413
507
  action: input.action,
414
508
  reason: input.reason ?? null,
509
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
415
510
  }),
416
511
  },
417
512
  );
418
- await publishSessionEventIds(deps, context.workspaceId, input.targetSessionId, [
419
- result.sessionControlEventId,
513
+ scheduleSessionCommandPostCommit(deps, "agent_control", [
514
+ {
515
+ kind: "session_event_fanout",
516
+ run: async () =>
517
+ await publishSessionEventIds(deps, context.workspaceId, input.targetSessionId, [
518
+ result.sessionControlEventId,
519
+ ]),
520
+ },
521
+ {
522
+ kind: "workspace_control_fanout",
523
+ run: async () =>
524
+ await publishWorkspaceControlEvent(
525
+ deps,
526
+ context.workspaceId,
527
+ result.workspaceControlEventId,
528
+ ),
529
+ },
530
+ {
531
+ kind: "workflow_wake",
532
+ run: async () => await requestControlWakeDispatch(deps, result.wakeCount),
533
+ },
534
+ {
535
+ kind: "workflow_wake",
536
+ run: async () => {
537
+ if (!result.workflowWake) return;
538
+ await wakeSessionCommand(deps, {
539
+ accountId: result.workflowWake.accountId,
540
+ workspaceId: result.workflowWake.workspaceId,
541
+ sessionId: result.workflowWake.sessionId,
542
+ workflowId: result.workflowWake.temporalWorkflowId,
543
+ wakeRevision: result.workflowWake.wakeRevision,
544
+ shouldSignal: true,
545
+ interruptionCount: result.interruptionCount,
546
+ controlRequested: true,
547
+ });
548
+ },
549
+ },
420
550
  ]);
421
- await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
422
- await requestControlWakeDispatch(deps, result.wakeCount);
423
551
  return { ...result, authorization };
424
552
  }
425
553
 
@@ -479,7 +607,7 @@ export async function moveHumanQueuePrompt(
479
607
  db: Database;
480
608
  bus: EventBus;
481
609
  sessionAuthorization?: SessionAuthorizationPort | null;
482
- },
610
+ } & SessionCommandPostCommitDeps,
483
611
  context: HumanSessionCommandContext,
484
612
  turnId: string,
485
613
  input: MoveSessionQueueItemRequest,
@@ -499,6 +627,7 @@ export async function moveHumanQueuePrompt(
499
627
  expectedQueueVersion: input.expectedQueueVersion,
500
628
  actor: { type: "human", subjectId: context.subjectId },
501
629
  operationKey: input.clientEventId,
630
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
502
631
  }),
503
632
  },
504
633
  );
@@ -511,7 +640,13 @@ export async function moveHumanQueuePrompt(
511
640
  authorization?.relatedSessionAccess ?? "root",
512
641
  ),
513
642
  };
514
- await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds);
643
+ scheduleSessionCommandPostCommit(deps, "human_queue_move", [
644
+ {
645
+ kind: "session_event_fanout",
646
+ run: async () =>
647
+ await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds),
648
+ },
649
+ ]);
515
650
  return response;
516
651
  }
517
652
 
@@ -520,7 +655,7 @@ export async function deleteHumanQueuePrompt(
520
655
  db: Database;
521
656
  bus: EventBus;
522
657
  sessionAuthorization?: SessionAuthorizationPort | null;
523
- },
658
+ } & SessionCommandPostCommitDeps,
524
659
  context: HumanSessionCommandContext,
525
660
  turnId: string,
526
661
  input: DeleteSessionQueueItemRequest,
@@ -540,6 +675,7 @@ export async function deleteHumanQueuePrompt(
540
675
  actor: { type: "human", subjectId: context.subjectId },
541
676
  operationKey: input.clientEventId,
542
677
  reason: input.reason ?? null,
678
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
543
679
  }),
544
680
  },
545
681
  );
@@ -552,7 +688,13 @@ export async function deleteHumanQueuePrompt(
552
688
  authorization?.relatedSessionAccess ?? "root",
553
689
  ),
554
690
  };
555
- await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds);
691
+ scheduleSessionCommandPostCommit(deps, "human_queue_delete", [
692
+ {
693
+ kind: "session_event_fanout",
694
+ run: async () =>
695
+ await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds),
696
+ },
697
+ ]);
556
698
  return response;
557
699
  }
558
700
 
@@ -561,7 +703,7 @@ export async function editHumanQueuePrompt(
561
703
  db: Database;
562
704
  bus: EventBus;
563
705
  sessionAuthorization?: SessionAuthorizationPort | null;
564
- },
706
+ } & SessionCommandPostCommitDeps,
565
707
  context: HumanSessionCommandContext,
566
708
  turnId: string,
567
709
  input: EditSessionQueueItemRequest,
@@ -582,6 +724,7 @@ export async function editHumanQueuePrompt(
582
724
  replaceDraft: input.replaceDraft,
583
725
  actor: { type: "human", subjectId: context.subjectId },
584
726
  operationKey: input.clientEventId,
727
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
585
728
  }),
586
729
  },
587
730
  );
@@ -595,7 +738,13 @@ export async function editHumanQueuePrompt(
595
738
  ),
596
739
  draft: composerDraft(result.draft)!,
597
740
  };
598
- await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds);
741
+ scheduleSessionCommandPostCommit(deps, "human_queue_edit", [
742
+ {
743
+ kind: "session_event_fanout",
744
+ run: async () =>
745
+ await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds),
746
+ },
747
+ ]);
599
748
  return response;
600
749
  }
601
750
 
@@ -603,8 +752,9 @@ export async function steerHumanQueuePrompt(
603
752
  deps: {
604
753
  db: Database;
605
754
  bus: EventBus;
755
+ workflowClient: Pick<SessionWorkflowClient, "wakeSessionWorkflow">;
606
756
  sessionAuthorization?: SessionAuthorizationPort | null;
607
- },
757
+ } & SessionCommandPostCommitDeps,
608
758
  context: HumanSessionCommandContext,
609
759
  turnId: string,
610
760
  input: SteerSessionQueueItemRequest,
@@ -624,6 +774,7 @@ export async function steerHumanQueuePrompt(
624
774
  controlEtag: input.controlEtag ?? null,
625
775
  actor: { type: "human", subjectId: context.subjectId },
626
776
  operationKey: input.clientEventId,
777
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
627
778
  }),
628
779
  },
629
780
  );
@@ -636,8 +787,36 @@ export async function steerHumanQueuePrompt(
636
787
  authorization?.relatedSessionAccess ?? "root",
637
788
  ),
638
789
  };
639
- await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds);
640
- await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
790
+ scheduleSessionCommandPostCommit(deps, "human_queue_steer", [
791
+ {
792
+ kind: "session_event_fanout",
793
+ run: async () =>
794
+ await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds),
795
+ },
796
+ {
797
+ kind: "workspace_control_fanout",
798
+ run: async () =>
799
+ await publishWorkspaceControlEvent(
800
+ deps,
801
+ context.workspaceId,
802
+ result.workspaceControlEventId,
803
+ ),
804
+ },
805
+ {
806
+ kind: "workflow_wake",
807
+ run: async () =>
808
+ await wakeSessionCommand(deps, {
809
+ accountId: context.accountId,
810
+ workspaceId: context.workspaceId,
811
+ sessionId: context.sessionId,
812
+ workflowId: result.workflowId,
813
+ wakeRevision: result.wakeRevision,
814
+ shouldSignal: true,
815
+ interruptionCount: result.interruptionCount,
816
+ controlRequested: true,
817
+ }),
818
+ },
819
+ ]);
641
820
  return response;
642
821
  }
643
822
 
@@ -645,9 +824,12 @@ export async function controlHumanSessionWorkstreamWithOutcome(
645
824
  deps: {
646
825
  db: Database;
647
826
  bus: EventBus;
648
- workflowClient: Pick<SessionWorkflowClient, "requestSessionWorkflowWakeDispatch">;
827
+ workflowClient: Pick<
828
+ SessionWorkflowClient,
829
+ "requestSessionWorkflowWakeDispatch" | "wakeSessionWorkflow"
830
+ >;
649
831
  sessionAuthorization?: SessionAuthorizationPort | null;
650
- },
832
+ } & SessionCommandPostCommitDeps,
651
833
  context: HumanSessionCommandContext,
652
834
  input: SessionControlRequest,
653
835
  ): Promise<{ response: SessionControlResponse; replay: boolean }> {
@@ -670,6 +852,7 @@ export async function controlHumanSessionWorkstreamWithOutcome(
670
852
  action: input.action,
671
853
  reason: input.reason ?? null,
672
854
  expectedControlEtag: input.expectedControlEtag ?? null,
855
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
673
856
  }),
674
857
  },
675
858
  );
@@ -685,11 +868,47 @@ export async function controlHumanSessionWorkstreamWithOutcome(
685
868
  cancelledSessionCount: result.cancelledSessionCount,
686
869
  cancelledTurnCount: result.cancelledTurnCount,
687
870
  };
688
- for (const affected of result.affectedSessionEvents) {
689
- await publishSessionEventIds(deps, context.workspaceId, affected.sessionId, affected.eventIds);
690
- }
691
- await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
692
- await requestControlWakeDispatch(deps, result.wakeCount);
871
+ scheduleSessionCommandPostCommit(deps, "human_control", [
872
+ ...result.affectedSessionEvents.map((affected) => ({
873
+ kind: "session_event_fanout" as const,
874
+ run: async () =>
875
+ await publishSessionEventIds(
876
+ deps,
877
+ context.workspaceId,
878
+ affected.sessionId,
879
+ affected.eventIds,
880
+ ),
881
+ })),
882
+ {
883
+ kind: "workspace_control_fanout",
884
+ run: async () =>
885
+ await publishWorkspaceControlEvent(
886
+ deps,
887
+ context.workspaceId,
888
+ result.workspaceControlEventId,
889
+ ),
890
+ },
891
+ {
892
+ kind: "workflow_wake",
893
+ run: async () => await requestControlWakeDispatch(deps, result.wakeCount),
894
+ },
895
+ {
896
+ kind: "workflow_wake",
897
+ run: async () => {
898
+ if (!result.workflowWake) return;
899
+ await wakeSessionCommand(deps, {
900
+ accountId: result.workflowWake.accountId,
901
+ workspaceId: result.workflowWake.workspaceId,
902
+ sessionId: result.workflowWake.sessionId,
903
+ workflowId: result.workflowWake.temporalWorkflowId,
904
+ wakeRevision: result.workflowWake.wakeRevision,
905
+ shouldSignal: true,
906
+ interruptionCount: result.interruptionCount,
907
+ controlRequested: true,
908
+ });
909
+ },
910
+ },
911
+ ]);
693
912
  return { response, replay: result.replay };
694
913
  }
695
914
 
@@ -707,7 +926,7 @@ export async function controlHumanWorkspace(
707
926
  db: Database;
708
927
  bus: EventBus;
709
928
  workflowClient: Pick<SessionWorkflowClient, "requestSessionWorkflowWakeDispatch">;
710
- },
929
+ } & SessionCommandPostCommitDeps,
711
930
  context: Omit<HumanSessionCommandContext, "sessionId">,
712
931
  input: WorkspaceInferenceControlRequest,
713
932
  ): Promise<WorkspaceInferenceControlResponse> {
@@ -721,6 +940,7 @@ export async function controlHumanWorkspace(
721
940
  action: input.action,
722
941
  reason: input.reason ?? null,
723
942
  expectedRevision: input.expectedRevision ?? null,
943
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
724
944
  }),
725
945
  ),
726
946
  );
@@ -731,8 +951,21 @@ export async function controlHumanWorkspace(
731
951
  interruptionCount: result.interruptionCount,
732
952
  wakeCount: result.wakeCount,
733
953
  };
734
- await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
735
- await requestControlWakeDispatch(deps, result.wakeCount);
954
+ scheduleSessionCommandPostCommit(deps, "human_workspace_control", [
955
+ {
956
+ kind: "workspace_control_fanout",
957
+ run: async () =>
958
+ await publishWorkspaceControlEvent(
959
+ deps,
960
+ context.workspaceId,
961
+ result.workspaceControlEventId,
962
+ ),
963
+ },
964
+ {
965
+ kind: "workflow_wake",
966
+ run: async () => await requestControlWakeDispatch(deps, result.wakeCount),
967
+ },
968
+ ]);
736
969
  return response;
737
970
  }
738
971
 
@@ -762,8 +995,8 @@ export async function getHumanComposerDraft(
762
995
  annotations: [],
763
996
  resources: [],
764
997
  model: session.model,
765
- reasoningEffort: reasoningEffortForMetadata(session.metadata, "medium"),
766
- latencyMode: latencyModeForMetadata(session.metadata, "standard"),
998
+ reasoningEffort: session.reasoningEffort,
999
+ latencyMode: session.latencyMode,
767
1000
  sourceTurnId: null,
768
1001
  sourceTurnVersion: null,
769
1002
  updatedAt: null,
@@ -794,6 +1027,7 @@ export async function saveHumanComposerDraft(
794
1027
  annotations,
795
1028
  resources: normalizeResources(input.resources),
796
1029
  subjectId: context.subjectId,
1030
+ controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
797
1031
  }),
798
1032
  ),
799
1033
  );