@dudousxd/nestjs-agent 0.6.0 → 0.8.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.
@@ -19,11 +19,24 @@ interface DispatchedLlmInput extends LlmStepEnvelope {
19
19
  * wire-contract change once the durable runtime exposes the attempt.
20
20
  */
21
21
  runId: string;
22
+ /** The turn index within the run (`llm:<step>`) — the `llm.turn` span's `step` metadata. */
23
+ step: number;
22
24
  /** The stream target: `input.sinkRunId ?? ctx.runId` of the dispatching workflow. */
23
25
  sinkRunId: string;
24
26
  /** True for a sub-agent run — wrap the writer with {@link childSinkWriter} (no end/fail). */
25
27
  childSink: boolean;
26
28
  }
29
+ /**
30
+ * Serializable input for the dispatched `tool` step: a {@link ToolStepEnvelope} plus the call
31
+ * identity the `tool.execution` span needs (`toolCallId`/`toolType`) — core's
32
+ * `AgentLoopHooks.dispatchTool` passes those on the separate `ToolCallRequest` argument, so the
33
+ * workflow folds them into the wire payload when it dispatches (same pattern as
34
+ * {@link DispatchedLlmInput}'s sink routing).
35
+ */
36
+ interface DispatchedToolInput extends ToolStepEnvelope {
37
+ toolCallId: string;
38
+ toolType: 'read' | 'action';
39
+ }
27
40
  /**
28
41
  * The two long steps `AgentRunWorkflow` dispatches as routed remote steps (`AgentRunSteps.llm` /
29
42
  * `AgentRunSteps.tool`) — the default under `durable: true`; `dispatchedSteps: false` runs them
@@ -47,7 +60,7 @@ declare class AgentRunSteps {
47
60
  * The timeout is applied HERE, handler-side, via `withToolTimeout` — never as a durable dispatch
48
61
  * `timeoutMs`, which bounds engine liveness (retryable) rather than the tool's own business outcome.
49
62
  */
50
- tool(input: ToolStepEnvelope): Promise<unknown>;
63
+ tool(input: DispatchedToolInput): Promise<unknown>;
51
64
  }
52
65
 
53
66
  /**
@@ -130,4 +143,4 @@ declare function agentDurable(options: AgentModuleOptions): DynamicModule[];
130
143
  */
131
144
  declare const AGENT_DISPATCHED_STEPS: unique symbol;
132
145
 
133
- export { AGENT_DISPATCHED_STEPS, AgentDurableModule, AgentRunSteps, AgentRunWorkflow, type DispatchedLlmInput, DurableAgentRunner, agentDurable };
146
+ export { AGENT_DISPATCHED_STEPS, AgentDurableModule, AgentRunSteps, AgentRunWorkflow, type DispatchedLlmInput, type DispatchedToolInput, DurableAgentRunner, agentDurable };
@@ -19,11 +19,24 @@ interface DispatchedLlmInput extends LlmStepEnvelope {
19
19
  * wire-contract change once the durable runtime exposes the attempt.
20
20
  */
21
21
  runId: string;
22
+ /** The turn index within the run (`llm:<step>`) — the `llm.turn` span's `step` metadata. */
23
+ step: number;
22
24
  /** The stream target: `input.sinkRunId ?? ctx.runId` of the dispatching workflow. */
23
25
  sinkRunId: string;
24
26
  /** True for a sub-agent run — wrap the writer with {@link childSinkWriter} (no end/fail). */
25
27
  childSink: boolean;
26
28
  }
29
+ /**
30
+ * Serializable input for the dispatched `tool` step: a {@link ToolStepEnvelope} plus the call
31
+ * identity the `tool.execution` span needs (`toolCallId`/`toolType`) — core's
32
+ * `AgentLoopHooks.dispatchTool` passes those on the separate `ToolCallRequest` argument, so the
33
+ * workflow folds them into the wire payload when it dispatches (same pattern as
34
+ * {@link DispatchedLlmInput}'s sink routing).
35
+ */
36
+ interface DispatchedToolInput extends ToolStepEnvelope {
37
+ toolCallId: string;
38
+ toolType: 'read' | 'action';
39
+ }
27
40
  /**
28
41
  * The two long steps `AgentRunWorkflow` dispatches as routed remote steps (`AgentRunSteps.llm` /
29
42
  * `AgentRunSteps.tool`) — the default under `durable: true`; `dispatchedSteps: false` runs them
@@ -47,7 +60,7 @@ declare class AgentRunSteps {
47
60
  * The timeout is applied HERE, handler-side, via `withToolTimeout` — never as a durable dispatch
48
61
  * `timeoutMs`, which bounds engine liveness (retryable) rather than the tool's own business outcome.
49
62
  */
50
- tool(input: ToolStepEnvelope): Promise<unknown>;
63
+ tool(input: DispatchedToolInput): Promise<unknown>;
51
64
  }
52
65
 
53
66
  /**
@@ -130,4 +143,4 @@ declare function agentDurable(options: AgentModuleOptions): DynamicModule[];
130
143
  */
131
144
  declare const AGENT_DISPATCHED_STEPS: unique symbol;
132
145
 
133
- export { AGENT_DISPATCHED_STEPS, AgentDurableModule, AgentRunSteps, AgentRunWorkflow, type DispatchedLlmInput, DurableAgentRunner, agentDurable };
146
+ export { AGENT_DISPATCHED_STEPS, AgentDurableModule, AgentRunSteps, AgentRunWorkflow, type DispatchedLlmInput, type DispatchedToolInput, DurableAgentRunner, agentDurable };
@@ -4,7 +4,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
4
4
  // src/durable/agent-run.workflow.ts
5
5
  import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY2, AGENT_STORE, QuotaExceededError, publishAgentRunFailed, runAgentLoop } from "@dudousxd/nestjs-agent-core";
6
6
  import { Workflow } from "@dudousxd/nestjs-durable";
7
- import { ContinueAsNew, WorkflowSuspended } from "@dudousxd/nestjs-durable-core";
7
+ import { isWorkflowControlFlowSignal } from "@dudousxd/nestjs-durable-core";
8
8
  import { Inject as Inject2, Injectable as Injectable2 } from "@nestjs/common";
9
9
 
10
10
  // src/agent-deps.ts
@@ -24,7 +24,7 @@ function childSinkWriter(writer) {
24
24
  __name(childSinkWriter, "childSinkWriter");
25
25
 
26
26
  // src/durable/agent-run.steps.ts
27
- import { AGENT_DEPS_FACTORY, withToolTimeout } from "@dudousxd/nestjs-agent-core";
27
+ import { AGENT_DEPS_FACTORY, traceLlmTurn, traceToolExecution, withToolTimeout } from "@dudousxd/nestjs-agent-core";
28
28
  import { Step } from "@dudousxd/nestjs-durable";
29
29
  import { Inject, Injectable } from "@nestjs/common";
30
30
  function _ts_decorate(decorators, target, key, desc) {
@@ -63,12 +63,12 @@ var AgentRunSteps = class {
63
63
  const tools = await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, deps.toolAllowList);
64
64
  const opened = await deps.sink.open(input.sinkRunId);
65
65
  const writer = input.childSink ? childSinkWriter(opened) : opened;
66
- return deps.model.runTurn({
66
+ return traceLlmTurn(input.runId, input.step, () => deps.model.runTurn({
67
67
  system: input.system,
68
68
  messages: input.messages,
69
69
  tools,
70
70
  sink: writer
71
- });
71
+ }));
72
72
  }
73
73
  /**
74
74
  * No retries — a tool may not be idempotent (a bare `@Step()` carries none, unlike `llm` above).
@@ -80,8 +80,14 @@ var AgentRunSteps = class {
80
80
  const ctx = {
81
81
  ...input.ctx
82
82
  };
83
- const invocation = deps.registry.invoke(input.toolName, input.input, ctx, deps.rolesPolicy);
84
- return input.timeoutMs !== void 0 ? withToolTimeout(invocation, input.timeoutMs, input.toolName) : invocation;
83
+ return traceToolExecution(input.ctx.runId, {
84
+ toolCallId: input.toolCallId,
85
+ toolName: input.toolName,
86
+ toolType: input.toolType
87
+ }, () => {
88
+ const invocation = deps.registry.invoke(input.toolName, input.input, ctx, deps.rolesPolicy);
89
+ return input.timeoutMs !== void 0 ? withToolTimeout(invocation, input.timeoutMs, input.toolName) : invocation;
90
+ });
85
91
  }
86
92
  };
87
93
  _ts_decorate([
@@ -98,7 +104,7 @@ _ts_decorate([
98
104
  Step(),
99
105
  _ts_metadata("design:type", Function),
100
106
  _ts_metadata("design:paramtypes", [
101
- typeof ToolStepEnvelope === "undefined" ? Object : ToolStepEnvelope
107
+ typeof DispatchedToolInput === "undefined" ? Object : DispatchedToolInput
102
108
  ]),
103
109
  _ts_metadata("design:returntype", Promise)
104
110
  ], AgentRunSteps.prototype, "tool", null);
@@ -159,8 +165,11 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
159
165
  openSink: /* @__PURE__ */ __name(async () => input.sinkRunId !== void 0 ? childSinkWriter(await deps.sink.open(sinkRunId)) : deps.sink.open(ctx.runId), "openSink"),
160
166
  awaitApproval: /* @__PURE__ */ __name((call) => ctx.waitForSignal(`tool:${ctx.runId}:${call.id}`), "awaitApproval"),
161
167
  step: /* @__PURE__ */ __name((name, fn) => ctx.localStep(name, fn), "step"),
162
- // Dispatched-step suspends must escape the loop's tool catch — control flow, not a tool failure.
163
- isControlFlowError: /* @__PURE__ */ __name((error) => error instanceof WorkflowSuspended || error instanceof ContinueAsNew, "isControlFlowError"),
168
+ // Dispatched-step suspends must escape the loop's tool catch — control flow, not a tool
169
+ // failure. Marker-based predicate, NOT instanceof: the signal's CLASS differs by runtime
170
+ // (engine in-process throws durable-core's WorkflowSuspended/ContinueAsNew; the BullMQ thin
171
+ // worker throws durable-worker's own Suspend), so only the Symbol.for marker is reliable.
172
+ isControlFlowError: /* @__PURE__ */ __name((error) => isWorkflowControlFlowSignal(error), "isControlFlowError"),
164
173
  runAgent: /* @__PURE__ */ __name(async (agentName, task) => {
165
174
  const subThreadId = await ctx.localStep(`subthread:${agentName}`, async () => {
166
175
  const thread = await this.store.createThread({
@@ -184,13 +193,23 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
184
193
  // execution. `sinkRunId`/`childSink` are sink routing this workflow already resolved above —
185
194
  // core's dispatchLlm signature stays sink-topology-agnostic, so we add them here, not in core.
186
195
  ...this.dispatchedSteps ? {
187
- dispatchLlm: /* @__PURE__ */ __name((_index, envelope) => ctx.step(this.steps.llm, {
196
+ dispatchLlm: /* @__PURE__ */ __name((index, envelope) => ctx.step(this.steps.llm, {
188
197
  ...envelope,
189
198
  runId: ctx.runId,
199
+ step: index,
190
200
  sinkRunId,
191
201
  childSink: input.sinkRunId !== void 0
192
202
  }), "dispatchLlm"),
193
- dispatchTool: /* @__PURE__ */ __name((_call, envelope) => ctx.step(this.steps.tool, envelope), "dispatchTool")
203
+ // Fold the call identity (`toolCallId`/`toolType`) into the wire payload — the handler
204
+ // emits the `tool.execution` span with it. Only read/action calls reach dispatchTool
205
+ // ('agent'-kind delegations branch to runAgent earlier in the loop), so anything that
206
+ // isn't 'action' is defensively 'read' — the same posture core takes for an
207
+ // unresolvable `kind`.
208
+ dispatchTool: /* @__PURE__ */ __name((call, envelope) => ctx.step(this.steps.tool, {
209
+ ...envelope,
210
+ toolCallId: call.id,
211
+ toolType: call.kind === "action" ? "action" : "read"
212
+ }), "dispatchTool")
194
213
  } : {}
195
214
  };
196
215
  try {
@@ -201,7 +220,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
201
220
  await ctx.localStep("deactivate", () => this.store.setActiveStream(input.threadId, null));
202
221
  return result;
203
222
  } catch (error) {
204
- if (error instanceof WorkflowSuspended || error instanceof ContinueAsNew) {
223
+ if (isWorkflowControlFlowSignal(error)) {
205
224
  throw error;
206
225
  }
207
226
  const message = error instanceof Error ? error.message : String(error);
@@ -248,7 +267,7 @@ AgentRunWorkflow = _ts_decorate2([
248
267
  // src/durable/durable-agent-runner.ts
249
268
  import { randomUUID } from "node:crypto";
250
269
  import { WorkflowService } from "@dudousxd/nestjs-durable";
251
- import { WorkflowSuspended as WorkflowSuspended2 } from "@dudousxd/nestjs-durable-core";
270
+ import { isWorkflowControlFlowSignal as isWorkflowControlFlowSignal2 } from "@dudousxd/nestjs-durable-core";
252
271
  import { Injectable as Injectable3 } from "@nestjs/common";
253
272
  function _ts_decorate3(decorators, target, key, desc) {
254
273
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -278,7 +297,7 @@ var DurableAgentRunner = class {
278
297
  try {
279
298
  await this.workflows.start(AgentRunWorkflow, stamped, runId);
280
299
  } catch (error) {
281
- if (!(error instanceof WorkflowSuspended2)) {
300
+ if (!isWorkflowControlFlowSignal2(error)) {
282
301
  throw error;
283
302
  }
284
303
  }