@dudousxd/nestjs-agent 1.15.0 → 1.16.1

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.
@@ -122,6 +122,7 @@ declare class AgentRunSteps {
122
122
  * across however many turns/retries it takes.
123
123
  */
124
124
  llm(input: DispatchedLlmInput): Promise<BufferedModelTurnResult>;
125
+ private runLlm;
125
126
  /**
126
127
  * NO DURABLE retries — a tool may not be idempotent (a bare `@Step()` carries none, unlike `llm`
127
128
  * above). What it does retry, in place, is a CLASSIFIED-TRANSIENT error from the tool's own
@@ -133,6 +134,7 @@ declare class AgentRunSteps {
133
134
  * ATTEMPT, so a retried invocation gets its own fresh timeout window.
134
135
  */
135
136
  tool(input: DispatchedToolInput): Promise<unknown>;
137
+ private runTool;
136
138
  }
137
139
 
138
140
  /**
@@ -122,6 +122,7 @@ declare class AgentRunSteps {
122
122
  * across however many turns/retries it takes.
123
123
  */
124
124
  llm(input: DispatchedLlmInput): Promise<BufferedModelTurnResult>;
125
+ private runLlm;
125
126
  /**
126
127
  * NO DURABLE retries — a tool may not be idempotent (a bare `@Step()` carries none, unlike `llm`
127
128
  * above). What it does retry, in place, is a CLASSIFIED-TRANSIENT error from the tool's own
@@ -133,6 +134,7 @@ declare class AgentRunSteps {
133
134
  * ATTEMPT, so a retried invocation gets its own fresh timeout window.
134
135
  */
135
136
  tool(input: DispatchedToolInput): Promise<unknown>;
137
+ private runTool;
136
138
  }
137
139
 
138
140
  /**
@@ -454,6 +454,19 @@ __name(queueFrame, "queueFrame");
454
454
  import { AGENT_DEPS_FACTORY, createFrameBuffer, createUiCollector, encodeStreamEvent as encodeStreamEvent2, invokeWithTransientRetry, observeTurnFrames, publishAgentToolRetry, stampToolKinds, traceLlmTurn, traceToolExecution, withAskTool, withMemoryTool, withSkillTool, withToolTimeout, withTurnFrames, wrapToolStepOutput } from "@dudousxd/nestjs-agent-core";
455
455
  import { Step } from "@dudousxd/nestjs-durable";
456
456
  import { Inject as Inject2, Injectable as Injectable2 } from "@nestjs/common";
457
+
458
+ // src/durable/outside-workflow-ctx.ts
459
+ import { runInWorkflowCtx } from "@dudousxd/nestjs-durable-core";
460
+ function outsideWorkflowCtx(fn) {
461
+ return runInWorkflowCtx(void 0, fn);
462
+ }
463
+ __name(outsideWorkflowCtx, "outsideWorkflowCtx");
464
+ function stepOf(ctx) {
465
+ return (name, fn, ...rest) => ctx.localStep(name, (...args) => outsideWorkflowCtx(() => fn(...args)), ...rest);
466
+ }
467
+ __name(stepOf, "stepOf");
468
+
469
+ // src/durable/agent-run.steps.ts
457
470
  function _ts_decorate2(decorators, target, key, desc) {
458
471
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
459
472
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -494,7 +507,10 @@ var AgentRunSteps = class {
494
507
  * double-writes. NEVER call `writer.end()`/`fail()` here: the workflow owns the stream's lifecycle,
495
508
  * across however many turns/retries it takes.
496
509
  */
497
- async llm(input) {
510
+ llm(input) {
511
+ return outsideWorkflowCtx(() => this.runLlm(input));
512
+ }
513
+ async runLlm(input) {
498
514
  const deps = this.factory.forAgent(input.agentName);
499
515
  const tools = withMemoryTool({
500
516
  tools: withSkillTool({
@@ -547,7 +563,10 @@ var AgentRunSteps = class {
547
563
  * bounds engine liveness (retryable) rather than the tool's own business outcome — and per
548
564
  * ATTEMPT, so a retried invocation gets its own fresh timeout window.
549
565
  */
550
- async tool(input) {
566
+ tool(input) {
567
+ return outsideWorkflowCtx(() => this.runTool(input));
568
+ }
569
+ async runTool(input) {
551
570
  const deps = this.factory.forAgent(input.ctx.agentName);
552
571
  if (deps.registry.spec(input.toolName)?.kind === "action" && input.toolType !== "action") {
553
572
  throw new Error(`tool "${input.toolName}" is an action tool but was dispatched without approval: the dispatching process resolved its kind against a registry that does not have it`);
@@ -599,7 +618,7 @@ _ts_decorate2([
599
618
  _ts_metadata2("design:paramtypes", [
600
619
  typeof DispatchedLlmInput === "undefined" ? Object : DispatchedLlmInput
601
620
  ]),
602
- _ts_metadata2("design:returntype", Promise)
621
+ _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
603
622
  ], AgentRunSteps.prototype, "llm", null);
604
623
  _ts_decorate2([
605
624
  Step(),
@@ -607,7 +626,7 @@ _ts_decorate2([
607
626
  _ts_metadata2("design:paramtypes", [
608
627
  typeof DispatchedToolInput === "undefined" ? Object : DispatchedToolInput
609
628
  ]),
610
- _ts_metadata2("design:returntype", Promise)
629
+ _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
611
630
  ], AgentRunSteps.prototype, "tool", null);
612
631
  AgentRunSteps = _ts_decorate2([
613
632
  Injectable2(),
@@ -677,7 +696,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
677
696
  if (!await ctx.patched("agent:chat-queue")) {
678
697
  return void 0;
679
698
  }
680
- const plan = await ctx.localStep(`queue:${outcome}`, async () => {
699
+ const plan = await stepOf(ctx)(`queue:${outcome}`, async () => {
681
700
  try {
682
701
  const settled = outcome === "completed" && await this.isCancelled(ctx.runId) ? "cancelled" : outcome;
683
702
  return await queue.plan({
@@ -704,7 +723,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
704
723
  throw failure;
705
724
  }
706
725
  const reason = failure instanceof Error ? failure.message : String(failure);
707
- return ctx.localStep("queue:restore", async () => {
726
+ return stepOf(ctx)("queue:restore", async () => {
708
727
  await queue.restore(input.threadId, next, "start_failed", reason);
709
728
  return queue.pausedFrame(input.threadId);
710
729
  });
@@ -746,7 +765,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
746
765
  if (delivery === void 0) {
747
766
  return;
748
767
  }
749
- await ctx.localStep("deliver:detached:unsettled", () => settleUnsettledDelegation({
768
+ await stepOf(ctx)("deliver:detached:unsettled", () => settleUnsettledDelegation({
750
769
  store: this.store,
751
770
  delivery,
752
771
  agent: input.agentName ?? "default",
@@ -762,7 +781,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
762
781
  const deps = this.factory.forAgent(input.agentName);
763
782
  const dispatchSteps = await ctx.patched("agent:dispatched-steps");
764
783
  if (input.sinkRunId !== void 0 || input.deliverTo !== void 0) {
765
- await ctx.localStep("activate", () => this.store.setActiveStream(input.threadId, ctx.runId));
784
+ await stepOf(ctx)("activate", () => this.store.setActiveStream(input.threadId, ctx.runId));
766
785
  }
767
786
  const sinkRunId = input.sinkRunId ?? ctx.runId;
768
787
  const chainBelow = [
@@ -812,7 +831,9 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
812
831
  // question set and an approval reach a parked run through one path, and the HTTP surface that
813
832
  // settles either one is the same `POST /agent/tool-call/*` family.
814
833
  awaitAnswers: /* @__PURE__ */ __name((request) => ctx.waitForSignal(`tool:${ctx.runId}:${request.id}`), "awaitAnswers"),
815
- step: /* @__PURE__ */ __name((name, fn) => ctx.localStep(name, fn), "step"),
834
+ // Bodies run outside the ambient workflow ctx, so nothing the application does in there can
835
+ // take a position in this run's journal (see `outsideWorkflowCtx`).
836
+ step: /* @__PURE__ */ __name((name, fn) => stepOf(ctx)(name, fn), "step"),
816
837
  // Both durable step primitives take their checkpoint position on the CALL, before their first
817
838
  // await, so launching a batch in one tick — what `settleAll` does — fixes the block of
818
839
  // positions in call order however the tools then settle. `ctx.patched` keeps a run that
@@ -826,7 +847,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
826
847
  // worker throws durable-worker's own Suspend), so only the Symbol.for marker is reliable.
827
848
  isControlFlowError: /* @__PURE__ */ __name((error) => isWorkflowControlFlowSignal(error), "isControlFlowError"),
828
849
  runAgent: /* @__PURE__ */ __name(async (agentName, task) => {
829
- const subThreadId = await ctx.localStep(`subthread:${agentName}`, async () => {
850
+ const subThreadId = await stepOf(ctx)(`subthread:${agentName}`, async () => {
830
851
  const thread = await this.store.createThread({
831
852
  actor: input.actor,
832
853
  transient: true
@@ -857,7 +878,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
857
878
  // - `deliverTo` instead. The parent's tool result is a receipt, so the answer needs an
858
879
  // address of its own, and by the time it exists nobody is holding one.
859
880
  startAgent: /* @__PURE__ */ __name(async ({ agentName, task, toolCallId: toolCallId2 }) => {
860
- const subThreadId = await ctx.localStep(`subthread:${agentName}`, async () => {
881
+ const subThreadId = await stepOf(ctx)(`subthread:${agentName}`, async () => {
861
882
  const thread = await this.store.createThread({
862
883
  actor: input.actor,
863
884
  transient: true
@@ -915,14 +936,14 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
915
936
  ...deps,
916
937
  day
917
938
  }, input, hooks);
918
- await ctx.localStep("deactivate", () => releaseThreadRun(this.store, input.threadId, ctx.runId));
939
+ await stepOf(ctx)("deactivate", () => releaseThreadRun(this.store, input.threadId, ctx.runId));
919
940
  return result;
920
941
  } catch (error) {
921
942
  if (isWorkflowControlFlowSignal(error)) {
922
943
  throw error;
923
944
  }
924
945
  if (error instanceof RunCancelledError) {
925
- await ctx.localStep("deactivate", () => releaseThreadRun(this.store, input.threadId, ctx.runId));
946
+ await stepOf(ctx)("deactivate", () => releaseThreadRun(this.store, input.threadId, ctx.runId));
926
947
  await this.settleDetachedParent(ctx, input, {
927
948
  status: "cancelled"
928
949
  });
@@ -947,14 +968,14 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
947
968
  code,
948
969
  message
949
970
  });
950
- await ctx.localStep("persist:run:fail", () => this.store.recordRunEnd?.({
971
+ await stepOf(ctx)("persist:run:fail", () => this.store.recordRunEnd?.({
951
972
  runId: ctx.runId,
952
973
  status: "failed",
953
974
  errorCode: code,
954
975
  errorMessage: message
955
976
  }) ?? Promise.resolve());
956
977
  const queueFrame2 = await this.advanceQueue(ctx, input, "failed", message);
957
- await ctx.localStep("deactivate", () => releaseThreadRun(this.store, input.threadId, ctx.runId));
978
+ await stepOf(ctx)("deactivate", () => releaseThreadRun(this.store, input.threadId, ctx.runId));
958
979
  await this.settleDetachedParent(ctx, input, {
959
980
  status: "failed",
960
981
  error: message
@@ -2007,6 +2028,59 @@ var AgentService = class {
2007
2028
  }
2008
2029
  return queue.publish(threadId);
2009
2030
  }
2031
+ /**
2032
+ * `POST <base>/queue/:messageId/interrupt` — run a message that is already waiting NOW: it moves
2033
+ * to the head of its queue marked as an interrupt, any pause is lifted, and the running turn is
2034
+ * cancelled (its settle starts this message — the same path a `mode: 'interrupt'` send takes).
2035
+ * With nothing running, the message starts at once.
2036
+ *
2037
+ * One request, and the message never leaves the queue: a client that removed it and sent it again
2038
+ * would lose it if the second call failed, and would run it twice if another tab's drain started
2039
+ * it in between. Answers the queue, plus `interrupting` (the run that was cancelled) or `runId`
2040
+ * (the run this message started).
2041
+ */
2042
+ async interruptQueuedMessage(actor, messageId) {
2043
+ const queue = this.requireQueue();
2044
+ const store = queue.queueStore();
2045
+ const threadId = await this.assertOwnsQueuedMessage(actor, messageId);
2046
+ const gone = /* @__PURE__ */ __name(() => new GoneException(`queued message ${messageId} already started or was removed`), "gone");
2047
+ const marked = await store.updateQueuedMessage(messageId, {
2048
+ interrupt: true
2049
+ });
2050
+ if (marked === null) {
2051
+ throw gone();
2052
+ }
2053
+ if (marked.interrupt !== true) {
2054
+ throw new NotImplementedException2("Interrupting a queued message requires a ChatQueueStore whose updateQueuedMessage stores `interrupt`; the bound store ignored it.");
2055
+ }
2056
+ if (!await store.moveQueuedMessage(messageId, 0)) {
2057
+ throw gone();
2058
+ }
2059
+ await store.setQueuePause(threadId, null);
2060
+ const { live } = await queue.holder(threadId, this.runner);
2061
+ if (live !== null && live !== messageId && await store.getQueuedMessage(messageId) !== null) {
2062
+ const state2 = await queue.publish(threadId);
2063
+ await this.runner.cancel(live);
2064
+ return {
2065
+ ...state2,
2066
+ interrupting: live
2067
+ };
2068
+ }
2069
+ if (live !== null) {
2070
+ return {
2071
+ ...await queue.state(threadId),
2072
+ runId: messageId
2073
+ };
2074
+ }
2075
+ const runId = await queue.kick(threadId, this.runner);
2076
+ const state = runId === void 0 ? await queue.publish(threadId) : await queue.state(threadId);
2077
+ return {
2078
+ ...state,
2079
+ ...runId !== void 0 ? {
2080
+ runId
2081
+ } : {}
2082
+ };
2083
+ }
2010
2084
  /** `DELETE <base>/threads/:id/queue` — drop every waiting message (and any pause). */
2011
2085
  async clearQueue(actor, threadId) {
2012
2086
  await this.assertOwnsThread(actor, threadId);
@@ -3649,6 +3723,14 @@ var QueueController = class {
3649
3723
  } : {}
3650
3724
  });
3651
3725
  }
3726
+ /**
3727
+ * Run a waiting message now: it moves to the head as an interrupt and the running turn is
3728
+ * cancelled for it. Carries `interrupting` (the cancelled run), or `runId` when nothing was
3729
+ * running and it started at once.
3730
+ */
3731
+ async interrupt(req, messageId) {
3732
+ return this.agent.interruptQueuedMessage(await this.actorResolver.resolve(req), messageId);
3733
+ }
3652
3734
  async remove(req, messageId) {
3653
3735
  return this.agent.removeQueuedMessage(await this.actorResolver.resolve(req), messageId);
3654
3736
  }
@@ -3699,6 +3781,17 @@ _ts_decorate16([
3699
3781
  ]),
3700
3782
  _ts_metadata15("design:returntype", Promise)
3701
3783
  ], QueueController.prototype, "update", null);
3784
+ _ts_decorate16([
3785
+ Post4("queue/:messageId/interrupt"),
3786
+ _ts_param14(0, Req7()),
3787
+ _ts_param14(1, Param4("messageId")),
3788
+ _ts_metadata15("design:type", Function),
3789
+ _ts_metadata15("design:paramtypes", [
3790
+ typeof Request === "undefined" ? Object : Request,
3791
+ String
3792
+ ]),
3793
+ _ts_metadata15("design:returntype", Promise)
3794
+ ], QueueController.prototype, "interrupt", null);
3702
3795
  _ts_decorate16([
3703
3796
  Delete2("queue/:messageId"),
3704
3797
  _ts_param14(0, Req7()),