@dudousxd/nestjs-agent 0.11.1 → 0.13.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.
@@ -1,8 +1,55 @@
1
- import { LlmStepEnvelope, ModelTurnResult, ToolStepEnvelope, AgentStore, AgentRunInput, AgentRunner, Decision } from '@dudousxd/nestjs-agent-core';
2
- import { WorkflowCtx } from '@dudousxd/nestjs-durable-core';
3
- import { A as AgentDepsFactory, a as AgentSurface, b as AgentModuleOptions } from '../agent-deps.factory-D7GdhqdK.cjs';
1
+ import { AgentStore, AgentRunInput, AgentLoopResult, LlmStepEnvelope, BufferedModelTurnResult, ToolStepEnvelope, AgentRunner, TokenStreamSink, HumanReply } from '@dudousxd/nestjs-agent-core';
2
+ import { RunGateway, WorkflowCtx } from '@dudousxd/nestjs-durable-core';
3
+ import { A as AgentDepsFactory, a as AgentSurface, b as AgentModuleOptions } from '../agent-deps.factory-y2hvoQ6U.cjs';
4
4
  import { WorkflowService } from '@dudousxd/nestjs-durable';
5
5
  import { DynamicModule } from '@nestjs/common';
6
+ import '@standard-schema/spec';
7
+
8
+ /**
9
+ * The agent turn AS a durable workflow. Persist/stream checkpoints are `ctx.localStep`s — the
10
+ * in-process primitive, NOT the always-dispatched `ctx.step`: their names are dynamic checkpoint
11
+ * identities (`persist:toolcall:<id>`), not routable worker groups, and their deps (store, sink)
12
+ * live in THIS workflow worker's DI. The two LONG steps (model call, tool execution) are dispatched
13
+ * through `AgentRunSteps` where the deployment asked for it (`AGENT_DISPATCHED_STEPS`, from
14
+ * `dispatchedSteps: true`) so the run isn't pinned to this pod while they execute; without that
15
+ * flag they are localSteps too. HITL is `ctx.waitForSignal`, and sub-agent delegation is
16
+ * `ctx.child(AgentRunWorkflow)` — a replay-safe, observable child run (it shows up as a node in the durable dashboard). A child
17
+ * streams into its top-level ancestor's sink (`sinkRunId`) so the human watching the parent sees it
18
+ * and can approve its action tools; the approval routes to the child's own run via `runForToolCall`.
19
+ */
20
+ declare class AgentRunWorkflow {
21
+ private readonly factory;
22
+ private readonly store;
23
+ private readonly dispatchedSteps;
24
+ private readonly runs?;
25
+ constructor(factory: AgentDepsFactory, store: AgentStore, dispatchedSteps: boolean, runs?: RunGateway | undefined);
26
+ /**
27
+ * Has someone asked this run to stop? Read from the runtime's own run status, which is where
28
+ * `DurableAgentRunner.cancel` puts it — `cancelling` while a compensating cancel is in flight,
29
+ * `cancelled` once it has settled.
30
+ *
31
+ * Costs a run read at each of the loop's observation points (two per step). That is a row the
32
+ * control plane already has, against a turn that is otherwise making model calls, and it buys the
33
+ * property that matters: the answer comes from a place BOTH the canceller and every pod that might
34
+ * replay this run can see.
35
+ *
36
+ * Failing to ask is not a cancel. A tenant with no gateway wired, or an operator briefly
37
+ * unreachable, answers "no" and the run carries on — the alternative is failing healthy runs
38
+ * because a status read timed out. The loop journals that answer like any other, so two processes
39
+ * can never disagree about what was seen here.
40
+ */
41
+ private isCancelled;
42
+ /**
43
+ * Settle the delegation a DETACHED run was started for, when that run ends without an answer.
44
+ *
45
+ * The loop delivers its own success (`deliver:detached`) but cannot catch its own crash, and the
46
+ * runtime settles the run row without knowing anything delegated it. Left alone, the card in the
47
+ * calling conversation says "started" for ever — the one state a reader can neither wait on nor
48
+ * act on. A no-op for every other run: only a detached one carries a delivery address.
49
+ */
50
+ private settleDetachedParent;
51
+ run(ctx: WorkflowCtx, input: AgentRunInput): Promise<AgentLoopResult>;
52
+ }
6
53
 
7
54
  /**
8
55
  * Serializable input for the dispatched `llm` step: a {@link LlmStepEnvelope} plus the sink routing
@@ -39,8 +86,8 @@ interface DispatchedToolInput extends ToolStepEnvelope {
39
86
  }
40
87
  /**
41
88
  * The two long steps `AgentRunWorkflow` dispatches as routed remote steps (`AgentRunSteps.llm` /
42
- * `AgentRunSteps.tool`) — the default under `durable: true`; `dispatchedSteps: false` runs them
43
- * in-process under `ctx.localStep` instead. Both re-resolve their deps from THIS worker's own DI via
89
+ * `AgentRunSteps.tool`) under `dispatchedSteps: true`; omitting that flag runs them in-process
90
+ * under `ctx.localStep` instead. Both re-resolve their deps from THIS worker's own DI via
44
91
  * `AGENT_DEPS_FACTORY.forAgent` — a step can be served by any worker in the fleet, not just the one
45
92
  * that started the run. Always provided by `AgentDurableModule` regardless of `dispatchedSteps`, so
46
93
  * the worker group is never orphaned.
@@ -54,7 +101,7 @@ declare class AgentRunSteps {
54
101
  * double-writes. NEVER call `writer.end()`/`fail()` here: the workflow owns the stream's lifecycle,
55
102
  * across however many turns/retries it takes.
56
103
  */
57
- llm(input: DispatchedLlmInput): Promise<ModelTurnResult>;
104
+ llm(input: DispatchedLlmInput): Promise<BufferedModelTurnResult>;
58
105
  /**
59
106
  * NO DURABLE retries — a tool may not be idempotent (a bare `@Step()` carries none, unlike `llm`
60
107
  * above). What it does retry, in place, is a CLASSIFIED-TRANSIENT error from the tool's own
@@ -68,42 +115,41 @@ declare class AgentRunSteps {
68
115
  tool(input: DispatchedToolInput): Promise<unknown>;
69
116
  }
70
117
 
71
- /**
72
- * The agent turn AS a durable workflow. Persist/stream checkpoints are `ctx.localStep`s — the
73
- * in-process primitive, NOT the always-dispatched `ctx.step`: their names are dynamic checkpoint
74
- * identities (`persist:toolcall:<id>`), not routable worker groups, and their deps (store, sink)
75
- * live in THIS workflow worker's DI. The two LONG steps (model call, tool execution) are dispatched
76
- * through `AgentRunSteps` by default (`AGENT_DISPATCHED_STEPS`, ON under `durable: true`) so the
77
- * run isn't pinned to this pod while they execute; `dispatchedSteps: false` keeps them localSteps
78
- * too. HITL is `ctx.waitForSignal`, and sub-agent delegation is `ctx.child(AgentRunWorkflow)` — a
79
- * replay-safe, observable child run (it shows up as a node in the durable dashboard). A child
80
- * streams into its top-level ancestor's sink (`sinkRunId`) so the human watching the parent sees it
81
- * and can approve its action tools; the approval routes to the child's own run via `runForToolCall`.
82
- */
83
- declare class AgentRunWorkflow {
84
- private readonly factory;
85
- private readonly store;
86
- private readonly steps;
87
- private readonly dispatchedSteps;
88
- constructor(factory: AgentDepsFactory, store: AgentStore, steps: AgentRunSteps | undefined, dispatchedSteps: boolean);
89
- run(ctx: WorkflowCtx, input: AgentRunInput): Promise<{
90
- text: string;
91
- }>;
92
- }
93
-
94
118
  /**
95
119
  * Runs the agent turn as a `@dudousxd/nestjs-durable` workflow. `start` creates the run and returns
96
- * its id immediately; a worker runs the body and streams tokens to the sink. HITL approval is
97
- * delivered as a durable signal namespaced by run, so it can never cross-resolve another run.
120
+ * its id immediately; a worker runs the body and streams tokens to the sink. Everything a human
121
+ * sends back into a parked run — a HITL approval, or the answers to a question set — is delivered as
122
+ * a durable signal namespaced by run, so it can never cross-resolve another run.
98
123
  */
99
124
  declare class DurableAgentRunner implements AgentRunner {
100
125
  private readonly workflows;
101
- constructor(workflows: WorkflowService);
126
+ private readonly runs;
127
+ private readonly store;
128
+ private readonly sink;
129
+ private readonly logger;
130
+ constructor(workflows: WorkflowService, runs: RunGateway, store: AgentStore, sink: TokenStreamSink);
102
131
  start(input: AgentRunInput): Promise<{
103
132
  runId: string;
104
133
  }>;
105
- signal(runId: string, toolCallId: string, decision: Decision): Promise<void>;
106
- cancel(_runId: string): Promise<void>;
134
+ signal(runId: string, toolCallId: string, reply: HumanReply): Promise<void>;
135
+ /**
136
+ * Stop a run, and settle what the durable runtime knows nothing about.
137
+ *
138
+ * `compensate: true` is the form that gives the BODY a chance: it moves the run to `cancelling`
139
+ * (which is what the workflow's own cancel observation reads) and then re-drives it, so a turn
140
+ * running in-process unwinds at its next safe point instead of finishing. It also guarantees the
141
+ * outcome for the turn that CANNOT observe anything — one parked on `waitForSignal`, suspended
142
+ * inside a position its journal already holds — because that re-drive re-suspends and the runtime
143
+ * settles the run `cancelled` regardless. The cascade to child runs comes with it.
144
+ *
145
+ * Neither of those settles the agent's own state, so this does: the run row gets its own terminal,
146
+ * and the subscriber gets a `cancelled` frame followed by a normal end. Both are keyed by runId
147
+ * alone, which is why they belong here rather than in the body — the body is the only place that
148
+ * knows which THREAD was streaming, so releasing that stays there (see `AgentRunWorkflow`).
149
+ *
150
+ * A tool already executing is not interrupted, here or anywhere: see `haltIfCancelled` in core.
151
+ */
152
+ cancel(runId: string): Promise<void>;
107
153
  }
108
154
 
109
155
  /**
@@ -174,10 +220,10 @@ declare function agentDurable(options: AgentModuleOptions): DynamicModule[];
174
220
  /**
175
221
  * DI flag carrying the EFFECTIVE `dispatchedSteps` value: whether `AgentRunWorkflow` dispatches its
176
222
  * `llm`/`tool` steps as routed remote steps (`AgentRunSteps`) instead of in-process `ctx.localStep`s.
177
- * `AgentDurableModule` derives it from `AGENT_OPTIONS` (`durable === true && dispatchedSteps !==
178
- * false` — default ON under durable); `AgentRunWorkflow` injects it. Kept in its own file —
179
- * `AgentDurableModule` provides `AgentRunWorkflow` and `AgentRunWorkflow` injects this token, so
180
- * declaring it in either of those two files would make them import each other.
223
+ * `AgentDurableModule` derives it from `AGENT_OPTIONS` (`durable === true && dispatchedSteps ===
224
+ * true`); `AgentRunWorkflow` injects it. Kept in its own file — `AgentDurableModule` provides
225
+ * `AgentRunWorkflow` and `AgentRunWorkflow` injects this token, so declaring it in either of those
226
+ * two files would make them import each other.
181
227
  */
182
228
  declare const AGENT_DISPATCHED_STEPS: unique symbol;
183
229
 
@@ -1,8 +1,55 @@
1
- import { LlmStepEnvelope, ModelTurnResult, ToolStepEnvelope, AgentStore, AgentRunInput, AgentRunner, Decision } from '@dudousxd/nestjs-agent-core';
2
- import { WorkflowCtx } from '@dudousxd/nestjs-durable-core';
3
- import { A as AgentDepsFactory, a as AgentSurface, b as AgentModuleOptions } from '../agent-deps.factory-D7GdhqdK.js';
1
+ import { AgentStore, AgentRunInput, AgentLoopResult, LlmStepEnvelope, BufferedModelTurnResult, ToolStepEnvelope, AgentRunner, TokenStreamSink, HumanReply } from '@dudousxd/nestjs-agent-core';
2
+ import { RunGateway, WorkflowCtx } from '@dudousxd/nestjs-durable-core';
3
+ import { A as AgentDepsFactory, a as AgentSurface, b as AgentModuleOptions } from '../agent-deps.factory-y2hvoQ6U.js';
4
4
  import { WorkflowService } from '@dudousxd/nestjs-durable';
5
5
  import { DynamicModule } from '@nestjs/common';
6
+ import '@standard-schema/spec';
7
+
8
+ /**
9
+ * The agent turn AS a durable workflow. Persist/stream checkpoints are `ctx.localStep`s — the
10
+ * in-process primitive, NOT the always-dispatched `ctx.step`: their names are dynamic checkpoint
11
+ * identities (`persist:toolcall:<id>`), not routable worker groups, and their deps (store, sink)
12
+ * live in THIS workflow worker's DI. The two LONG steps (model call, tool execution) are dispatched
13
+ * through `AgentRunSteps` where the deployment asked for it (`AGENT_DISPATCHED_STEPS`, from
14
+ * `dispatchedSteps: true`) so the run isn't pinned to this pod while they execute; without that
15
+ * flag they are localSteps too. HITL is `ctx.waitForSignal`, and sub-agent delegation is
16
+ * `ctx.child(AgentRunWorkflow)` — a replay-safe, observable child run (it shows up as a node in the durable dashboard). A child
17
+ * streams into its top-level ancestor's sink (`sinkRunId`) so the human watching the parent sees it
18
+ * and can approve its action tools; the approval routes to the child's own run via `runForToolCall`.
19
+ */
20
+ declare class AgentRunWorkflow {
21
+ private readonly factory;
22
+ private readonly store;
23
+ private readonly dispatchedSteps;
24
+ private readonly runs?;
25
+ constructor(factory: AgentDepsFactory, store: AgentStore, dispatchedSteps: boolean, runs?: RunGateway | undefined);
26
+ /**
27
+ * Has someone asked this run to stop? Read from the runtime's own run status, which is where
28
+ * `DurableAgentRunner.cancel` puts it — `cancelling` while a compensating cancel is in flight,
29
+ * `cancelled` once it has settled.
30
+ *
31
+ * Costs a run read at each of the loop's observation points (two per step). That is a row the
32
+ * control plane already has, against a turn that is otherwise making model calls, and it buys the
33
+ * property that matters: the answer comes from a place BOTH the canceller and every pod that might
34
+ * replay this run can see.
35
+ *
36
+ * Failing to ask is not a cancel. A tenant with no gateway wired, or an operator briefly
37
+ * unreachable, answers "no" and the run carries on — the alternative is failing healthy runs
38
+ * because a status read timed out. The loop journals that answer like any other, so two processes
39
+ * can never disagree about what was seen here.
40
+ */
41
+ private isCancelled;
42
+ /**
43
+ * Settle the delegation a DETACHED run was started for, when that run ends without an answer.
44
+ *
45
+ * The loop delivers its own success (`deliver:detached`) but cannot catch its own crash, and the
46
+ * runtime settles the run row without knowing anything delegated it. Left alone, the card in the
47
+ * calling conversation says "started" for ever — the one state a reader can neither wait on nor
48
+ * act on. A no-op for every other run: only a detached one carries a delivery address.
49
+ */
50
+ private settleDetachedParent;
51
+ run(ctx: WorkflowCtx, input: AgentRunInput): Promise<AgentLoopResult>;
52
+ }
6
53
 
7
54
  /**
8
55
  * Serializable input for the dispatched `llm` step: a {@link LlmStepEnvelope} plus the sink routing
@@ -39,8 +86,8 @@ interface DispatchedToolInput extends ToolStepEnvelope {
39
86
  }
40
87
  /**
41
88
  * The two long steps `AgentRunWorkflow` dispatches as routed remote steps (`AgentRunSteps.llm` /
42
- * `AgentRunSteps.tool`) — the default under `durable: true`; `dispatchedSteps: false` runs them
43
- * in-process under `ctx.localStep` instead. Both re-resolve their deps from THIS worker's own DI via
89
+ * `AgentRunSteps.tool`) under `dispatchedSteps: true`; omitting that flag runs them in-process
90
+ * under `ctx.localStep` instead. Both re-resolve their deps from THIS worker's own DI via
44
91
  * `AGENT_DEPS_FACTORY.forAgent` — a step can be served by any worker in the fleet, not just the one
45
92
  * that started the run. Always provided by `AgentDurableModule` regardless of `dispatchedSteps`, so
46
93
  * the worker group is never orphaned.
@@ -54,7 +101,7 @@ declare class AgentRunSteps {
54
101
  * double-writes. NEVER call `writer.end()`/`fail()` here: the workflow owns the stream's lifecycle,
55
102
  * across however many turns/retries it takes.
56
103
  */
57
- llm(input: DispatchedLlmInput): Promise<ModelTurnResult>;
104
+ llm(input: DispatchedLlmInput): Promise<BufferedModelTurnResult>;
58
105
  /**
59
106
  * NO DURABLE retries — a tool may not be idempotent (a bare `@Step()` carries none, unlike `llm`
60
107
  * above). What it does retry, in place, is a CLASSIFIED-TRANSIENT error from the tool's own
@@ -68,42 +115,41 @@ declare class AgentRunSteps {
68
115
  tool(input: DispatchedToolInput): Promise<unknown>;
69
116
  }
70
117
 
71
- /**
72
- * The agent turn AS a durable workflow. Persist/stream checkpoints are `ctx.localStep`s — the
73
- * in-process primitive, NOT the always-dispatched `ctx.step`: their names are dynamic checkpoint
74
- * identities (`persist:toolcall:<id>`), not routable worker groups, and their deps (store, sink)
75
- * live in THIS workflow worker's DI. The two LONG steps (model call, tool execution) are dispatched
76
- * through `AgentRunSteps` by default (`AGENT_DISPATCHED_STEPS`, ON under `durable: true`) so the
77
- * run isn't pinned to this pod while they execute; `dispatchedSteps: false` keeps them localSteps
78
- * too. HITL is `ctx.waitForSignal`, and sub-agent delegation is `ctx.child(AgentRunWorkflow)` — a
79
- * replay-safe, observable child run (it shows up as a node in the durable dashboard). A child
80
- * streams into its top-level ancestor's sink (`sinkRunId`) so the human watching the parent sees it
81
- * and can approve its action tools; the approval routes to the child's own run via `runForToolCall`.
82
- */
83
- declare class AgentRunWorkflow {
84
- private readonly factory;
85
- private readonly store;
86
- private readonly steps;
87
- private readonly dispatchedSteps;
88
- constructor(factory: AgentDepsFactory, store: AgentStore, steps: AgentRunSteps | undefined, dispatchedSteps: boolean);
89
- run(ctx: WorkflowCtx, input: AgentRunInput): Promise<{
90
- text: string;
91
- }>;
92
- }
93
-
94
118
  /**
95
119
  * Runs the agent turn as a `@dudousxd/nestjs-durable` workflow. `start` creates the run and returns
96
- * its id immediately; a worker runs the body and streams tokens to the sink. HITL approval is
97
- * delivered as a durable signal namespaced by run, so it can never cross-resolve another run.
120
+ * its id immediately; a worker runs the body and streams tokens to the sink. Everything a human
121
+ * sends back into a parked run — a HITL approval, or the answers to a question set — is delivered as
122
+ * a durable signal namespaced by run, so it can never cross-resolve another run.
98
123
  */
99
124
  declare class DurableAgentRunner implements AgentRunner {
100
125
  private readonly workflows;
101
- constructor(workflows: WorkflowService);
126
+ private readonly runs;
127
+ private readonly store;
128
+ private readonly sink;
129
+ private readonly logger;
130
+ constructor(workflows: WorkflowService, runs: RunGateway, store: AgentStore, sink: TokenStreamSink);
102
131
  start(input: AgentRunInput): Promise<{
103
132
  runId: string;
104
133
  }>;
105
- signal(runId: string, toolCallId: string, decision: Decision): Promise<void>;
106
- cancel(_runId: string): Promise<void>;
134
+ signal(runId: string, toolCallId: string, reply: HumanReply): Promise<void>;
135
+ /**
136
+ * Stop a run, and settle what the durable runtime knows nothing about.
137
+ *
138
+ * `compensate: true` is the form that gives the BODY a chance: it moves the run to `cancelling`
139
+ * (which is what the workflow's own cancel observation reads) and then re-drives it, so a turn
140
+ * running in-process unwinds at its next safe point instead of finishing. It also guarantees the
141
+ * outcome for the turn that CANNOT observe anything — one parked on `waitForSignal`, suspended
142
+ * inside a position its journal already holds — because that re-drive re-suspends and the runtime
143
+ * settles the run `cancelled` regardless. The cascade to child runs comes with it.
144
+ *
145
+ * Neither of those settles the agent's own state, so this does: the run row gets its own terminal,
146
+ * and the subscriber gets a `cancelled` frame followed by a normal end. Both are keyed by runId
147
+ * alone, which is why they belong here rather than in the body — the body is the only place that
148
+ * knows which THREAD was streaming, so releasing that stays there (see `AgentRunWorkflow`).
149
+ *
150
+ * A tool already executing is not interrupted, here or anywhere: see `haltIfCancelled` in core.
151
+ */
152
+ cancel(runId: string): Promise<void>;
107
153
  }
108
154
 
109
155
  /**
@@ -174,10 +220,10 @@ declare function agentDurable(options: AgentModuleOptions): DynamicModule[];
174
220
  /**
175
221
  * DI flag carrying the EFFECTIVE `dispatchedSteps` value: whether `AgentRunWorkflow` dispatches its
176
222
  * `llm`/`tool` steps as routed remote steps (`AgentRunSteps`) instead of in-process `ctx.localStep`s.
177
- * `AgentDurableModule` derives it from `AGENT_OPTIONS` (`durable === true && dispatchedSteps !==
178
- * false` — default ON under durable); `AgentRunWorkflow` injects it. Kept in its own file —
179
- * `AgentDurableModule` provides `AgentRunWorkflow` and `AgentRunWorkflow` injects this token, so
180
- * declaring it in either of those two files would make them import each other.
223
+ * `AgentDurableModule` derives it from `AGENT_OPTIONS` (`durable === true && dispatchedSteps ===
224
+ * true`); `AgentRunWorkflow` injects it. Kept in its own file — `AgentDurableModule` provides
225
+ * `AgentRunWorkflow` and `AgentRunWorkflow` injects this token, so declaring it in either of those
226
+ * two files would make them import each other.
181
227
  */
182
228
  declare const AGENT_DISPATCHED_STEPS: unique symbol;
183
229