@opengeni/core 0.4.7 → 0.4.8

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.
package/dist/index.js CHANGED
@@ -1,3 +1,8 @@
1
+ // src/workflow-wake-contract.ts
2
+ var SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID = "opengeni-session-workflow-wake-dispatcher";
3
+ var SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE = "sessionWorkflowWakeDispatcherWorkflow";
4
+ var SESSION_WORKFLOW_WAKE_DISPATCHER_PERIOD_MS = 1e4;
5
+
1
6
  // src/sandbox/fleet.ts
2
7
  import {
3
8
  getEnrollment,
@@ -558,7 +563,9 @@ async function delegatedAccessContext(c, deps, mode, token = bearerToken(c)) {
558
563
  ...payload.sessionId ? { sessionId: payload.sessionId } : {},
559
564
  // Caller identity: the turn that minted this token. Tools classify the
560
565
  // CALLER from this instead of re-reading the live active pointer.
561
- ...payload.turnId ? { turnId: payload.turnId } : {}
566
+ ...payload.turnId ? { turnId: payload.turnId } : {},
567
+ ...payload.attemptId ? { attemptId: payload.attemptId } : {},
568
+ ...payload.executionGeneration ? { executionGeneration: payload.executionGeneration } : {}
562
569
  }
563
570
  }
564
571
  ],
@@ -2596,7 +2603,6 @@ import {
2596
2603
  import {
2597
2604
  createSession,
2598
2605
  createSessionWithIdempotencyKey,
2599
- enqueueSessionMessageAtomically,
2600
2606
  encryptVariableSetValue as encryptVariableSetValue2,
2601
2607
  getAnySessionInGroup,
2602
2608
  getEnrollment as getEnrollment2,
@@ -2607,17 +2613,23 @@ import {
2607
2613
  getSandbox as getSandbox3,
2608
2614
  getSession,
2609
2615
  getSessionByCreateIdempotencyKey,
2616
+ getSessionEvent,
2617
+ getWorkspaceControlEvent,
2610
2618
  getSessionLineage,
2611
2619
  getSessionTurn,
2612
2620
  getWorkspaceModelPolicy,
2613
2621
  initializeSessionStartAtomically,
2614
2622
  requireSession as requireSession2,
2623
+ submitHumanPromptInTransaction,
2615
2624
  updateSessionTitle as updateSessionTitleRow,
2616
- SessionQueueConflictError
2625
+ withWorkspaceSubjectRls,
2626
+ QueueCommandConflictError,
2627
+ SessionControlConflictError
2617
2628
  } from "@opengeni/db";
2618
2629
  import {
2619
2630
  appendAndPublishEvents,
2620
- publishDurableSessionEvents
2631
+ publishDurableSessionEvents,
2632
+ publishDurableWorkspaceControlEvent
2621
2633
  } from "@opengeni/events";
2622
2634
  import { HTTPException as HTTPException9 } from "hono/http-exception";
2623
2635
  var reservedSessionMcpServerIds = /* @__PURE__ */ new Set(["opengeni", "files", "docs", "codex_apps"]);
@@ -2942,30 +2954,37 @@ async function postUserMessageTurn(input) {
2942
2954
  const requestedReasoningEffort = input.reasoningEffort ?? null;
2943
2955
  assertConfiguredModel(settings, requestedModel);
2944
2956
  await assertWorkspaceModelPolicyAllows(db, settings, workspaceId, requestedModel);
2957
+ const operationKey = input.clientEventId ?? crypto.randomUUID();
2945
2958
  let result;
2946
2959
  try {
2947
- result = await enqueueSessionMessageAtomically(db, {
2948
- accountId,
2960
+ result = await withWorkspaceSubjectRls(
2961
+ db,
2949
2962
  workspaceId,
2950
- sessionId,
2951
- actor: input.actor ?? accountId,
2952
- origin: input.origin ?? "human",
2953
- text: input.text,
2954
- resources: input.resources,
2955
- tools: input.tools,
2956
- model: requestedModel,
2957
- reasoningEffort: requestedReasoningEffort,
2958
- clientEventId: input.clientEventId ?? null,
2959
- mcpCredentialUpdates: input.mcpCredentialUpdates ?? [],
2960
- delivery: input.delivery ?? "queue",
2961
- ...input.expectedControlGeneration !== void 0 ? { expectedControlGeneration: input.expectedControlGeneration } : {},
2962
- ...input.expectedWorkspaceInferenceGeneration !== void 0 ? {
2963
- expectedWorkspaceInferenceGeneration: input.expectedWorkspaceInferenceGeneration
2964
- } : {},
2965
- reasoningEffortFallback: settings.openaiReasoningEffort
2966
- });
2963
+ input.actor ?? accountId,
2964
+ (scoped) => scoped.transaction(
2965
+ (tx) => submitHumanPromptInTransaction(tx, {
2966
+ accountId,
2967
+ workspaceId,
2968
+ sessionId,
2969
+ subjectId: input.actor ?? accountId,
2970
+ actor: { type: "human", subjectId: input.actor ?? accountId },
2971
+ operationKey,
2972
+ delivery: input.delivery ?? "send",
2973
+ controlEtag: input.controlEtag ?? null,
2974
+ expectedDraftRevision: input.expectedDraftRevision ?? null,
2975
+ text: input.text,
2976
+ resources: input.resources,
2977
+ tools: input.tools,
2978
+ model: requestedModel,
2979
+ reasoningEffort: requestedReasoningEffort,
2980
+ reasoningEffortFallback: settings.openaiReasoningEffort,
2981
+ source: input.origin === "operator" ? "api" : "user",
2982
+ mcpCredentialUpdates: input.mcpCredentialUpdates ?? []
2983
+ })
2984
+ )
2985
+ );
2967
2986
  } catch (error) {
2968
- if (error instanceof SessionQueueConflictError) {
2987
+ if (error instanceof QueueCommandConflictError || error instanceof SessionControlConflictError) {
2969
2988
  throw new HTTPException9(409, { message: error.message });
2970
2989
  }
2971
2990
  if (error instanceof Error && error.message.includes("cancelled")) {
@@ -2976,35 +2995,51 @@ async function postUserMessageTurn(input) {
2976
2995
  }
2977
2996
  throw error;
2978
2997
  }
2979
- await bus.publish(workspaceId, sessionId, result.events);
2980
- if (result.shouldSignalControl && result.controlEvent) {
2981
- if (result.workflowWakeRevision === null) {
2982
- throw new Error("Steer control has no workflow wake revision");
2983
- }
2984
- await workflowClient.signalSessionControl({
2985
- accountId,
2998
+ const events = await Promise.all(
2999
+ result.eventIds.map((eventId) => getSessionEvent(db, workspaceId, eventId))
3000
+ );
3001
+ if (events.some((event) => event === null)) {
3002
+ throw new Error("Committed prompt events could not be reloaded");
3003
+ }
3004
+ const turn = await getSessionTurn(db, workspaceId, result.turnId);
3005
+ if (!turn) throw new Error("Committed prompt turn could not be reloaded");
3006
+ const accepted = events.find((event) => event?.id === result.acceptedEventId);
3007
+ if (!accepted) throw new Error("Committed user.message event could not be reloaded");
3008
+ await publishDurableSessionEvents(
3009
+ bus,
3010
+ workspaceId,
3011
+ sessionId,
3012
+ events.filter((event) => event !== null)
3013
+ );
3014
+ if (result.workspaceControlEventId) {
3015
+ const controlEvent = await getWorkspaceControlEvent(
3016
+ db,
2986
3017
  workspaceId,
2987
- sessionId,
2988
- eventId: result.controlEvent.id,
2989
- workflowId: result.temporalWorkflowId,
2990
- workflowWakeRevision: result.workflowWakeRevision
2991
- });
2992
- } else if (result.shouldWake) {
2993
- if (result.workflowWakeRevision === null) {
2994
- throw new Error("Runnable prompt has no workflow wake revision");
3018
+ result.workspaceControlEventId
3019
+ );
3020
+ if (!controlEvent) {
3021
+ throw new Error(
3022
+ `Committed workspace control event disappeared: ${result.workspaceControlEventId}`
3023
+ );
2995
3024
  }
3025
+ await publishDurableWorkspaceControlEvent(bus, workspaceId, controlEvent);
3026
+ }
3027
+ try {
2996
3028
  await workflowClient.wakeSessionWorkflow({
2997
3029
  accountId,
2998
3030
  workspaceId,
2999
3031
  sessionId,
3000
- workflowId: result.temporalWorkflowId,
3001
- wakeRevision: result.workflowWakeRevision
3032
+ workflowId: turn.temporalWorkflowId,
3033
+ wakeRevision: result.wakeRevision,
3034
+ ...result.interruptionCount > 0 ? { interruptionRequested: true } : {}
3002
3035
  });
3036
+ } catch (error) {
3037
+ console.warn(
3038
+ `[sessions] workflow wake failed for committed prompt ${workspaceId}/${sessionId}; durable outbox will retry`,
3039
+ error
3040
+ );
3003
3041
  }
3004
- return {
3005
- accepted: result.accepted,
3006
- turn: result.turn
3007
- };
3042
+ return { accepted, turn };
3008
3043
  }
3009
3044
  async function createSessionForRequest(deps, grant, workspaceId, rawPayload) {
3010
3045
  const { settings, db, bus, workflowClient, objectStorage } = deps;
@@ -3290,13 +3325,11 @@ async function acceptSessionUserMessage(deps, grant, workspaceId, sessionId, inp
3290
3325
  model: input.model ?? null,
3291
3326
  reasoningEffort: input.reasoningEffort ?? null,
3292
3327
  mcpCredentialUpdates,
3293
- delivery: input.delivery ?? "queue",
3328
+ delivery: input.delivery ?? "send",
3294
3329
  origin: input.origin ?? "human",
3295
3330
  actor: grant.subjectId,
3296
- ...input.expectedControlGeneration !== void 0 ? { expectedControlGeneration: input.expectedControlGeneration } : {},
3297
- ...input.expectedWorkspaceInferenceGeneration !== void 0 ? {
3298
- expectedWorkspaceInferenceGeneration: input.expectedWorkspaceInferenceGeneration
3299
- } : {},
3331
+ ...input.controlEtag !== void 0 ? { controlEtag: input.controlEtag } : {},
3332
+ ...input.expectedDraftRevision !== void 0 ? { expectedDraftRevision: input.expectedDraftRevision } : {},
3300
3333
  ...input.clientEventId ? { clientEventId: input.clientEventId } : {}
3301
3334
  });
3302
3335
  await recordWorkspaceUsage(deps, {
@@ -3611,6 +3644,394 @@ function assertWorkspaceDeletable(input) {
3611
3644
  });
3612
3645
  }
3613
3646
  }
3647
+
3648
+ // src/application/session-commands.ts
3649
+ import { reasoningEffortForMetadata as reasoningEffortForMetadata2 } from "@opengeni/contracts";
3650
+ import {
3651
+ deleteSessionQueueItemInTransaction,
3652
+ editQueuedTurnInTransaction,
3653
+ getComposerDraftInTransaction,
3654
+ getSession as getSession2,
3655
+ getSessionEvent as getSessionEvent2,
3656
+ getWorkspaceControlEvent as getWorkspaceControlEvent2,
3657
+ getSessionQueueSnapshot,
3658
+ moveQueuedTurnInTransaction,
3659
+ mutateSessionControlInTransaction,
3660
+ mutateWorkspaceControlInTransaction,
3661
+ saveComposerDraftInTransaction,
3662
+ sendAgentMessageInTransaction,
3663
+ serializeEffectiveSessionControl,
3664
+ steerAgentSessionInTransaction,
3665
+ steerQueuedTurnInTransaction,
3666
+ withWorkspaceRls,
3667
+ withWorkspaceSubjectRls as withWorkspaceSubjectRls2
3668
+ } from "@opengeni/db";
3669
+ import {
3670
+ publishDurableSessionEvents as publishDurableSessionEvents2,
3671
+ publishDurableWorkspaceControlEvent as publishDurableWorkspaceControlEvent2
3672
+ } from "@opengeni/events";
3673
+ function agentActor(context) {
3674
+ return {
3675
+ type: "agent_attempt",
3676
+ sessionId: context.callerSessionId,
3677
+ turnId: context.callerTurnId,
3678
+ attemptId: context.callerAttemptId,
3679
+ executionGeneration: context.callerExecutionGeneration
3680
+ };
3681
+ }
3682
+ async function publishAndWakeAgentCommand(deps, input) {
3683
+ await publishSessionEventIds(deps, input.workspaceId, input.sessionId, input.eventIds);
3684
+ if (!input.shouldSignal || input.wakeRevision === null) return;
3685
+ try {
3686
+ await deps.workflowClient.wakeSessionWorkflow({
3687
+ accountId: input.accountId,
3688
+ workspaceId: input.workspaceId,
3689
+ sessionId: input.sessionId,
3690
+ workflowId: input.workflowId,
3691
+ wakeRevision: input.wakeRevision,
3692
+ ...input.interruptionCount > 0 ? { interruptionRequested: true } : {}
3693
+ });
3694
+ } catch (error) {
3695
+ console.warn(
3696
+ `[session-commands] immediate Agent command wake failed for ${input.workspaceId}/${input.sessionId}; durable outbox will retry`,
3697
+ error
3698
+ );
3699
+ }
3700
+ }
3701
+ async function requestControlWakeDispatch(deps, wakeCount) {
3702
+ if (wakeCount === 0) return;
3703
+ try {
3704
+ await deps.workflowClient.requestSessionWorkflowWakeDispatch();
3705
+ } catch (error) {
3706
+ console.warn(
3707
+ `[session-commands] immediate control wake dispatch failed for ${wakeCount} committed revisions; durable outbox will retry`,
3708
+ error
3709
+ );
3710
+ }
3711
+ }
3712
+ async function publishSessionEventIds(deps, workspaceId, sessionId, eventIds) {
3713
+ if (eventIds.length === 0) return;
3714
+ const events = await Promise.all(
3715
+ eventIds.map((eventId) => getSessionEvent2(deps.db, workspaceId, eventId))
3716
+ );
3717
+ await publishDurableSessionEvents2(
3718
+ deps.bus,
3719
+ workspaceId,
3720
+ sessionId,
3721
+ events.filter((event) => event !== null)
3722
+ );
3723
+ }
3724
+ async function publishWorkspaceControlEvent(deps, workspaceId, eventId) {
3725
+ if (!eventId) return;
3726
+ const event = await getWorkspaceControlEvent2(deps.db, workspaceId, eventId);
3727
+ if (!event) {
3728
+ throw new Error(`Committed workspace control event disappeared: ${eventId}`);
3729
+ }
3730
+ await publishDurableWorkspaceControlEvent2(deps.bus, workspaceId, event);
3731
+ }
3732
+ async function sendAgentSessionMessage(deps, context, input) {
3733
+ const result = await withWorkspaceRls(
3734
+ deps.db,
3735
+ context.workspaceId,
3736
+ (scoped) => scoped.transaction(
3737
+ (tx) => sendAgentMessageInTransaction(tx, {
3738
+ accountId: context.accountId,
3739
+ workspaceId: context.workspaceId,
3740
+ targetSessionId: input.targetSessionId,
3741
+ actor: agentActor(context),
3742
+ operationKey: input.idempotencyKey,
3743
+ text: input.text
3744
+ })
3745
+ )
3746
+ );
3747
+ await publishAndWakeAgentCommand(deps, {
3748
+ accountId: context.accountId,
3749
+ workspaceId: context.workspaceId,
3750
+ sessionId: input.targetSessionId,
3751
+ eventIds: result.eventIds,
3752
+ workflowId: result.workflowId,
3753
+ wakeRevision: result.wakeRevision,
3754
+ shouldSignal: result.shouldSignal,
3755
+ interruptionCount: 0
3756
+ });
3757
+ await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
3758
+ return result;
3759
+ }
3760
+ async function steerAgentSession(deps, context, input) {
3761
+ const result = await withWorkspaceRls(
3762
+ deps.db,
3763
+ context.workspaceId,
3764
+ (scoped) => scoped.transaction(
3765
+ (tx) => steerAgentSessionInTransaction(tx, {
3766
+ accountId: context.accountId,
3767
+ workspaceId: context.workspaceId,
3768
+ targetSessionId: input.targetSessionId,
3769
+ actor: agentActor(context),
3770
+ operationKey: input.idempotencyKey,
3771
+ instruction: input.instruction
3772
+ })
3773
+ )
3774
+ );
3775
+ await publishAndWakeAgentCommand(deps, {
3776
+ accountId: context.accountId,
3777
+ workspaceId: context.workspaceId,
3778
+ sessionId: input.targetSessionId,
3779
+ eventIds: result.eventIds,
3780
+ workflowId: result.workflowId,
3781
+ wakeRevision: result.wakeRevision,
3782
+ shouldSignal: result.shouldSignal,
3783
+ interruptionCount: result.interruptionCount
3784
+ });
3785
+ await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
3786
+ return result;
3787
+ }
3788
+ async function controlAgentSessionWorkstream(deps, context, input) {
3789
+ const result = await withWorkspaceRls(
3790
+ deps.db,
3791
+ context.workspaceId,
3792
+ (scoped) => scoped.transaction(
3793
+ (tx) => mutateSessionControlInTransaction(tx, {
3794
+ accountId: context.accountId,
3795
+ workspaceId: context.workspaceId,
3796
+ sessionId: input.targetSessionId,
3797
+ actor: agentActor(context),
3798
+ operationKey: input.idempotencyKey,
3799
+ action: input.action,
3800
+ reason: input.reason ?? null
3801
+ })
3802
+ )
3803
+ );
3804
+ await publishSessionEventIds(deps, context.workspaceId, input.targetSessionId, [
3805
+ result.sessionControlEventId
3806
+ ]);
3807
+ await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
3808
+ await requestControlWakeDispatch(deps, result.wakeCount);
3809
+ return result;
3810
+ }
3811
+ function receipt(row) {
3812
+ return {
3813
+ id: row.id,
3814
+ action: row.action,
3815
+ operationKey: row.operationKey,
3816
+ targetSessionId: row.targetSessionId,
3817
+ targetTurnId: row.targetTurnId,
3818
+ appliedControlRevision: row.appliedControlRevision,
3819
+ appliedQueueVersion: row.appliedQueueVersion,
3820
+ appliedTurnVersion: row.appliedTurnVersion,
3821
+ appliedDraftRevision: row.appliedDraftRevision,
3822
+ createdAt: row.createdAt.toISOString()
3823
+ };
3824
+ }
3825
+ function composerDraft(row) {
3826
+ if (!row) return null;
3827
+ return {
3828
+ revision: row.revision,
3829
+ text: row.text,
3830
+ resources: row.resources,
3831
+ tools: row.tools,
3832
+ model: row.model,
3833
+ reasoningEffort: row.reasoningEffort,
3834
+ sourceTurnId: row.sourceTurnId,
3835
+ sourceTurnVersion: row.sourceTurnVersion,
3836
+ updatedAt: row.updatedAt.toISOString()
3837
+ };
3838
+ }
3839
+ async function authoritativeQueue(db, workspaceId, sessionId) {
3840
+ const snapshot = await getSessionQueueSnapshot(db, workspaceId, sessionId);
3841
+ if (!snapshot) throw new Error(`Session not found: ${sessionId}`);
3842
+ return snapshot;
3843
+ }
3844
+ async function moveHumanQueuePrompt(deps, context, turnId, input) {
3845
+ const result = await withWorkspaceRls(
3846
+ deps.db,
3847
+ context.workspaceId,
3848
+ (scoped) => scoped.transaction(
3849
+ (tx) => moveQueuedTurnInTransaction(tx, {
3850
+ ...context,
3851
+ turnId,
3852
+ beforeTurnId: input.beforeTurnId,
3853
+ expectedQueueVersion: input.expectedQueueVersion,
3854
+ actor: { type: "human", subjectId: context.subjectId },
3855
+ operationKey: input.clientEventId
3856
+ })
3857
+ )
3858
+ );
3859
+ const response = {
3860
+ receipt: receipt(result.receipt),
3861
+ snapshot: await authoritativeQueue(deps.db, context.workspaceId, context.sessionId)
3862
+ };
3863
+ await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds);
3864
+ return response;
3865
+ }
3866
+ async function deleteHumanQueuePrompt(deps, context, turnId, input) {
3867
+ const result = await withWorkspaceRls(
3868
+ deps.db,
3869
+ context.workspaceId,
3870
+ (scoped) => scoped.transaction(
3871
+ (tx) => deleteSessionQueueItemInTransaction(tx, {
3872
+ ...context,
3873
+ turnId,
3874
+ expectedTurnVersion: input.expectedTurnVersion,
3875
+ actor: { type: "human", subjectId: context.subjectId },
3876
+ operationKey: input.clientEventId,
3877
+ reason: input.reason ?? null
3878
+ })
3879
+ )
3880
+ );
3881
+ const response = {
3882
+ receipt: receipt(result.receipt),
3883
+ snapshot: await authoritativeQueue(deps.db, context.workspaceId, context.sessionId)
3884
+ };
3885
+ await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds);
3886
+ return response;
3887
+ }
3888
+ async function editHumanQueuePrompt(deps, context, turnId, input) {
3889
+ const result = await withWorkspaceSubjectRls2(
3890
+ deps.db,
3891
+ context.workspaceId,
3892
+ context.subjectId,
3893
+ (scoped) => scoped.transaction(
3894
+ (tx) => editQueuedTurnInTransaction(tx, {
3895
+ ...context,
3896
+ turnId,
3897
+ expectedTurnVersion: input.expectedTurnVersion,
3898
+ expectedDraftRevision: input.expectedDraftRevision,
3899
+ replaceDraft: input.replaceDraft,
3900
+ actor: { type: "human", subjectId: context.subjectId },
3901
+ operationKey: input.clientEventId
3902
+ })
3903
+ )
3904
+ );
3905
+ const response = {
3906
+ receipt: receipt(result.receipt),
3907
+ snapshot: await authoritativeQueue(deps.db, context.workspaceId, context.sessionId),
3908
+ draft: composerDraft(result.draft)
3909
+ };
3910
+ await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds);
3911
+ return response;
3912
+ }
3913
+ async function steerHumanQueuePrompt(deps, context, turnId, input) {
3914
+ const result = await withWorkspaceRls(
3915
+ deps.db,
3916
+ context.workspaceId,
3917
+ (scoped) => scoped.transaction(
3918
+ (tx) => steerQueuedTurnInTransaction(tx, {
3919
+ ...context,
3920
+ turnId,
3921
+ expectedTurnVersion: input.expectedTurnVersion,
3922
+ controlEtag: input.controlEtag ?? null,
3923
+ actor: { type: "human", subjectId: context.subjectId },
3924
+ operationKey: input.clientEventId
3925
+ })
3926
+ )
3927
+ );
3928
+ const response = {
3929
+ receipt: receipt(result.receipt),
3930
+ snapshot: await authoritativeQueue(deps.db, context.workspaceId, context.sessionId)
3931
+ };
3932
+ await publishSessionEventIds(deps, context.workspaceId, context.sessionId, result.eventIds);
3933
+ await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
3934
+ return response;
3935
+ }
3936
+ async function controlHumanSessionWorkstream(deps, context, input) {
3937
+ const result = await withWorkspaceRls(
3938
+ deps.db,
3939
+ context.workspaceId,
3940
+ (scoped) => scoped.transaction(
3941
+ (tx) => mutateSessionControlInTransaction(tx, {
3942
+ accountId: context.accountId,
3943
+ workspaceId: context.workspaceId,
3944
+ sessionId: context.sessionId,
3945
+ actor: { type: "human", subjectId: context.subjectId },
3946
+ operationKey: input.clientEventId,
3947
+ action: input.action,
3948
+ reason: input.reason ?? null,
3949
+ expectedControlEtag: input.expectedControlEtag ?? null
3950
+ })
3951
+ )
3952
+ );
3953
+ const response = {
3954
+ receipt: receipt(result.receipt),
3955
+ effectiveControl: serializeEffectiveSessionControl(result.control),
3956
+ interruptionCount: result.interruptionCount,
3957
+ wakeCount: result.wakeCount
3958
+ };
3959
+ await publishSessionEventIds(deps, context.workspaceId, context.sessionId, [
3960
+ result.sessionControlEventId
3961
+ ]);
3962
+ await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
3963
+ await requestControlWakeDispatch(deps, result.wakeCount);
3964
+ return response;
3965
+ }
3966
+ async function controlHumanWorkspace(deps, context, input) {
3967
+ const result = await withWorkspaceRls(
3968
+ deps.db,
3969
+ context.workspaceId,
3970
+ (scoped) => scoped.transaction(
3971
+ (tx) => mutateWorkspaceControlInTransaction(tx, {
3972
+ accountId: context.accountId,
3973
+ workspaceId: context.workspaceId,
3974
+ actor: { type: "human", subjectId: context.subjectId },
3975
+ operationKey: input.clientEventId,
3976
+ action: input.action,
3977
+ reason: input.reason ?? null,
3978
+ expectedRevision: input.expectedRevision ?? null
3979
+ })
3980
+ )
3981
+ );
3982
+ const response = {
3983
+ receipt: receipt(result.receipt),
3984
+ state: result.workspaceState,
3985
+ revision: result.revision,
3986
+ interruptionCount: result.interruptionCount,
3987
+ wakeCount: result.wakeCount
3988
+ };
3989
+ await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
3990
+ await requestControlWakeDispatch(deps, result.wakeCount);
3991
+ return response;
3992
+ }
3993
+ async function getHumanComposerDraft(db, context) {
3994
+ const row = await withWorkspaceSubjectRls2(
3995
+ db,
3996
+ context.workspaceId,
3997
+ context.subjectId,
3998
+ (scoped) => getComposerDraftInTransaction(scoped, {
3999
+ workspaceId: context.workspaceId,
4000
+ sessionId: context.sessionId,
4001
+ subjectId: context.subjectId
4002
+ })
4003
+ );
4004
+ const mapped = composerDraft(row);
4005
+ if (mapped) return mapped;
4006
+ const session = await getSession2(db, context.workspaceId, context.sessionId);
4007
+ if (!session) throw new Error(`Session not found: ${context.sessionId}`);
4008
+ return {
4009
+ revision: 0,
4010
+ text: "",
4011
+ resources: [],
4012
+ tools: [],
4013
+ model: session.model,
4014
+ reasoningEffort: reasoningEffortForMetadata2(session.metadata, "medium"),
4015
+ sourceTurnId: null,
4016
+ sourceTurnVersion: null,
4017
+ updatedAt: null
4018
+ };
4019
+ }
4020
+ async function saveHumanComposerDraft(db, context, input) {
4021
+ const row = await withWorkspaceSubjectRls2(
4022
+ db,
4023
+ context.workspaceId,
4024
+ context.subjectId,
4025
+ (scoped) => scoped.transaction(
4026
+ (tx) => saveComposerDraftInTransaction(tx, {
4027
+ ...context,
4028
+ ...input,
4029
+ subjectId: context.subjectId
4030
+ })
4031
+ )
4032
+ );
4033
+ return composerDraft(row);
4034
+ }
3614
4035
  export {
3615
4036
  MARKETING_SOCIAL_PACK_ID,
3616
4037
  MAX_CHECKS_PER_RIG,
@@ -3619,6 +4040,9 @@ export {
3619
4040
  MAX_ENVIRONMENTS_PER_WORKSPACE,
3620
4041
  MAX_RIGS_PER_WORKSPACE,
3621
4042
  MAX_VARIABLES_PER_ENVIRONMENT,
4043
+ SESSION_WORKFLOW_WAKE_DISPATCHER_PERIOD_MS,
4044
+ SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID,
4045
+ SESSION_WORKFLOW_WAKE_DISPATCHER_WORKFLOW_TYPE,
3622
4046
  acceptSessionUserMessage,
3623
4047
  activateRigVersionForApi,
3624
4048
  appendRigSetupCommand,
@@ -3635,18 +4059,24 @@ export {
3635
4059
  buildMarketingDailyAnalysisAgentConfig,
3636
4060
  checkLimit,
3637
4061
  classifyRigVerificationOutcome,
4062
+ controlAgentSessionWorkstream,
4063
+ controlHumanSessionWorkstream,
4064
+ controlHumanWorkspace,
3638
4065
  createAndStartSession,
3639
4066
  createCatalogItem,
3640
4067
  createRigForApi,
3641
4068
  createRigVersionForApi,
3642
4069
  createSessionForRequest,
3643
4070
  createValidatedScheduledTask,
4071
+ deleteHumanQueuePrompt,
3644
4072
  deleteRigForApi,
3645
4073
  disableCapability,
3646
4074
  discoverMcpRegistryCapabilities,
4075
+ editHumanQueuePrompt,
3647
4076
  enableCapability,
3648
4077
  enabledCapabilityMcpToolRefs,
3649
4078
  getCapabilityPack,
4079
+ getHumanComposerDraft,
3650
4080
  hasPermission,
3651
4081
  isBuiltInCapabilityPack,
3652
4082
  isUserMember,
@@ -3660,6 +4090,7 @@ export {
3660
4090
  memberCanAdminister,
3661
4091
  mergeResourceRefs,
3662
4092
  mergeToolRefs,
4093
+ moveHumanQueuePrompt,
3663
4094
  normalizeResources,
3664
4095
  officialMcpRegistryUrl,
3665
4096
  postUserMessageTurn,
@@ -3691,13 +4122,17 @@ export {
3691
4122
  rigActorForGrant,
3692
4123
  routingEnabled,
3693
4124
  runOnSandbox,
4125
+ saveHumanComposerDraft,
3694
4126
  scheduledTaskTemporalScheduleId,
3695
4127
  scheduledTaskToolsProvided,
3696
4128
  scheduledTaskTriggerToken,
4129
+ sendAgentSessionMessage,
3697
4130
  settingsWithEnabledCapabilityMcpServers,
3698
4131
  settingsWithMcpCapabilityServers,
3699
4132
  settingsWithSessionMcpServerMetadata,
3700
4133
  stableJson,
4134
+ steerAgentSession,
4135
+ steerHumanQueuePrompt,
3701
4136
  swapActiveSandbox,
3702
4137
  syncCreatedScheduledTask,
3703
4138
  syncUpdatedScheduledTask,