@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.
- package/README.md +2 -0
- package/dist/durable/index.cjs +114 -21
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +2 -0
- package/dist/durable/index.d.ts +2 -0
- package/dist/durable/index.js +108 -15
- package/dist/durable/index.js.map +1 -1
- package/dist/index.cjs +72 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/durable/index.d.cts
CHANGED
|
@@ -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
|
/**
|
package/dist/durable/index.d.ts
CHANGED
|
@@ -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
|
/**
|
package/dist/durable/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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()),
|