@dudousxd/nestjs-agent-core 0.16.0 → 0.18.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.
package/README.md CHANGED
@@ -35,6 +35,8 @@ import type { ModelProvider, AgentStore, ToolSpec, RolesPolicy } from '@dudousxd
35
35
  - `RunCancelledError` / `AgentLoopHooks.cancelled()` — real cancellation. The loop asks `hooks.cancelled()` at the points where stopping is safe and cheap (between steps, before the next model call, before a turn's tools are dispatched), ALWAYS from inside a checkpoint, so the answer is journaled and a cancel arriving between two replays can never change a branch a replayed position already took; the positions themselves sit behind `hooks.patched('agent:cancellation')`, so a run already in flight keeps its recorded shape. A tool already executing is never interrupted — there is no un-executing a side effect, and abandoning a dispatched step leaves a journal holding a dispatch whose result never lands. Observing a cancel throws `RunCancelledError`, which unwinds through the path a suspend already uses; the RUNNER settles it, recording the run `cancelled` (`AgentStore.recordRunEnd`'s third terminal, never `failed`) and ending the stream on a `{ kind: 'cancelled' }` frame rather than failing it, so a user pressing Stop is not in anybody's error rate.
36
36
  - `isControlFlowSignal(error)` / `isReplayIntegrityError(error)` — recognize a durable suspend and a checkpoint refusal without importing the durable packages (a `Symbol.for` marker and a class name, respectively). Rethrow both untouched from any `catch` in a workflow body.
37
37
  - `settleAll(tasks)` / `SettledTask<T>` — the implementation behind the optional `AgentLoopHooks.parallel`, which is how a turn's `read` tool calls run concurrently. It invokes every task synchronously, in list order, before awaiting any of them (so a runner that takes checkpoint positions on the call keeps them in call order), and resolves only once all of them have settled (so a runner that unwinds a turn by throwing never abandons a sibling mid-dispatch). A runner that assigns positions anywhere else simply omits the hook and the loop stays sequential.
38
+ - `AgentStreamEvent` / `encodeStreamEvent` / `decodeStreamEvent` — the live-stream vocabulary (text, reasoning, tool calls with optional `parentId` nesting, `elicitation`, `approval-requested` with `approver`/`expiresAt`, server-pushed `ui` components, `title`, `cancelled`). It is also the contract a runner that is not this loop writes to get the React client for free: see [docs/stream-protocol.md](../../docs/stream-protocol.md). `AgentUiComponent` and `AgentApprovalRequest` are the payload types.
39
+ - `observeTurnFrames` / `withTurnFrames` — what a model turn streamed that its result does not carry: the model's thinking (`reasoning` text, and `reasoningMs` summed over each burst of consecutive reasoning frames) and pushed `ui` components (first-seen order, last props per id). The loop and the dispatched `llm` step wrap the provider's sink with it inside the model checkpoint, so every provider that streams the vocabulary gets reasoning persisted (`StoredMessage.reasoning` / `reasoningMs` / `ui`) and a replay reads the journaled numbers back. A provider may set `ModelTurnResult.reasoning` / `reasoningMs` / `ui` itself; its values win. The step's `reasoningMs` also rides the `step-finish` frame.
38
40
  - `runAgentLoop(deps, input, hooks)` — the loop; the NestJS package drives it from both runners
39
41
 
40
42
  ## Guardrails — `@dudousxd/nestjs-agent-core/guardrails`
@@ -1,4 +1,4 @@
1
- import { A as Actor, I as InputProcessor, O as OutputProcessor, T as ToolHandler } from '../tool-CL9oEytW.cjs';
1
+ import { A as Actor, I as InputProcessor, O as OutputProcessor, T as ToolHandler } from '../tool-CHIw-aTx.cjs';
2
2
  import '@standard-schema/spec';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { A as Actor, I as InputProcessor, O as OutputProcessor, T as ToolHandler } from '../tool-CL9oEytW.js';
1
+ import { A as Actor, I as InputProcessor, O as OutputProcessor, T as ToolHandler } from '../tool-CHIw-aTx.js';
2
2
  import '@standard-schema/spec';
3
3
 
4
4
  /**
package/dist/index.cjs CHANGED
@@ -119,6 +119,7 @@ __export(src_exports, {
119
119
  memoryWriteVerdict: () => memoryWriteVerdict,
120
120
  normalizeDelegation: () => normalizeDelegation,
121
121
  normalizeElicitationReply: () => normalizeElicitationReply,
122
+ observeTurnFrames: () => observeTurnFrames,
122
123
  offerMemories: () => offerMemories,
123
124
  offerSkills: () => offerSkills,
124
125
  publishAgentDelegated: () => publishAgentDelegated,
@@ -168,6 +169,7 @@ __export(src_exports, {
168
169
  withMemoryTool: () => withMemoryTool,
169
170
  withSkillTool: () => withSkillTool,
170
171
  withToolTimeout: () => withToolTimeout,
172
+ withTurnFrames: () => withTurnFrames,
171
173
  writeMemory: () => writeMemory
172
174
  });
173
175
  module.exports = __toCommonJS(src_exports);
@@ -229,6 +231,105 @@ function decodeStreamEvent(line) {
229
231
  }
230
232
  __name(decodeStreamEvent, "decodeStreamEvent");
231
233
 
234
+ // src/turn-frames.ts
235
+ function observeTurnFrames(writer, now = Date.now) {
236
+ const decoder2 = new TextDecoder();
237
+ let pending = "";
238
+ let reasoning = "";
239
+ let reasoningMs = 0;
240
+ let burstStartedAt;
241
+ const ui = /* @__PURE__ */ new Map();
242
+ function closeBurst() {
243
+ if (burstStartedAt !== void 0) {
244
+ reasoningMs += Math.max(0, now() - burstStartedAt);
245
+ burstStartedAt = void 0;
246
+ }
247
+ }
248
+ __name(closeBurst, "closeBurst");
249
+ function observeLine(line) {
250
+ if (line.trim().length === 0) {
251
+ return;
252
+ }
253
+ const event = decodeStreamEvent(line);
254
+ if (event === null) {
255
+ return;
256
+ }
257
+ if (event.kind === "reasoning") {
258
+ burstStartedAt ??= now();
259
+ reasoning += event.text;
260
+ return;
261
+ }
262
+ closeBurst();
263
+ if (event.kind === "ui") {
264
+ ui.set(event.id, {
265
+ id: event.id,
266
+ component: event.component,
267
+ props: event.props,
268
+ ...event.version !== void 0 ? {
269
+ version: event.version
270
+ } : {}
271
+ });
272
+ }
273
+ }
274
+ __name(observeLine, "observeLine");
275
+ function observe(chunk) {
276
+ pending += decoder2.decode(chunk, {
277
+ stream: true
278
+ });
279
+ let newline = pending.indexOf("\n");
280
+ while (newline !== -1) {
281
+ observeLine(pending.slice(0, newline));
282
+ pending = pending.slice(newline + 1);
283
+ newline = pending.indexOf("\n");
284
+ }
285
+ }
286
+ __name(observe, "observe");
287
+ return {
288
+ writer: {
289
+ write(chunk) {
290
+ observe(chunk);
291
+ return writer.write(chunk);
292
+ },
293
+ end: /* @__PURE__ */ __name(() => writer.end(), "end"),
294
+ fail: /* @__PURE__ */ __name((error) => writer.fail(error), "fail")
295
+ },
296
+ summary() {
297
+ if (pending.length > 0) {
298
+ observeLine(pending);
299
+ pending = "";
300
+ }
301
+ closeBurst();
302
+ return {
303
+ ...reasoning.length > 0 ? {
304
+ reasoning,
305
+ reasoningMs: Math.round(reasoningMs)
306
+ } : {},
307
+ ...ui.size > 0 ? {
308
+ ui: [
309
+ ...ui.values()
310
+ ]
311
+ } : {}
312
+ };
313
+ }
314
+ };
315
+ }
316
+ __name(observeTurnFrames, "observeTurnFrames");
317
+ function withTurnFrames(result, frames) {
318
+ return {
319
+ ...result,
320
+ ...result.reasoning === void 0 && frames.reasoning !== void 0 ? {
321
+ reasoning: frames.reasoning
322
+ } : {},
323
+ ...result.reasoningMs === void 0 && frames.reasoningMs !== void 0 ? {
324
+ reasoningMs: frames.reasoningMs
325
+ } : {},
326
+ ...result.ui === void 0 && frames.ui !== void 0 ? {
327
+ ui: frames.ui
328
+ } : {}
329
+ };
330
+ }
331
+ __name(withTurnFrames, "withTurnFrames");
332
+
232
333
  // src/spi/pricing-store.ts
233
334
  async function seedModelPrices(store, prices) {
234
335
  for (const price of prices) {
@@ -3534,12 +3635,13 @@ ${block}`;
3534
3635
  writer
3535
3636
  }) : void 0;
3536
3637
  const buffer = gateMode === "whole" ? createFrameBuffer() : void 0;
3537
- const result = stampToolKinds(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
3638
+ const frames = observeTurnFrames(incremental?.writer ?? buffer?.writer ?? writer);
3639
+ const result = stampToolKinds(withTurnFrames(await traceLlmTurn(hooks.runId, i, () => deps.model.runTurn({
3538
3640
  system: prompt.system,
3539
3641
  messages: prompt.messages,
3540
3642
  tools,
3541
- sink: incremental?.writer ?? buffer?.writer ?? writer
3542
- })), deps);
3643
+ sink: frames.writer
3644
+ })), frames.summary()), deps);
3543
3645
  if (incremental !== void 0) {
3544
3646
  await incremental.settled();
3545
3647
  const refusal = incremental.rejection();
@@ -3727,6 +3829,15 @@ ${block}`;
3727
3829
  ...input.agentName !== void 0 ? {
3728
3830
  agentName: input.agentName
3729
3831
  } : {},
3832
+ ...turn.reasoning !== void 0 ? {
3833
+ reasoning: turn.reasoning
3834
+ } : {},
3835
+ ...turn.reasoningMs !== void 0 ? {
3836
+ reasoningMs: turn.reasoningMs
3837
+ } : {},
3838
+ ...turn.ui !== void 0 && turn.ui.length > 0 ? {
3839
+ ui: turn.ui
3840
+ } : {},
3730
3841
  ...messageCalls.length > 0 ? {
3731
3842
  toolCalls: messageCalls
3732
3843
  } : {},
@@ -3784,7 +3895,10 @@ ${block}`;
3784
3895
  await writer.write(encodeStreamEvent({
3785
3896
  kind: "step-finish",
3786
3897
  usage: turn.usage,
3787
- costUsd
3898
+ costUsd,
3899
+ ...turn.reasoningMs !== void 0 ? {
3900
+ reasoningMs: turn.reasoningMs
3901
+ } : {}
3788
3902
  }));
3789
3903
  });
3790
3904
  break;
@@ -3837,7 +3951,10 @@ ${block}`;
3837
3951
  await writer.write(encodeStreamEvent({
3838
3952
  kind: "step-finish",
3839
3953
  usage: turn.usage,
3840
- costUsd
3954
+ costUsd,
3955
+ ...turn.reasoningMs !== void 0 ? {
3956
+ reasoningMs: turn.reasoningMs
3957
+ } : {}
3841
3958
  }));
3842
3959
  });
3843
3960
  }
@@ -3992,6 +4109,7 @@ __name(runAgentLoop, "runAgentLoop");
3992
4109
  memoryWriteVerdict,
3993
4110
  normalizeDelegation,
3994
4111
  normalizeElicitationReply,
4112
+ observeTurnFrames,
3995
4113
  offerMemories,
3996
4114
  offerSkills,
3997
4115
  publishAgentDelegated,
@@ -4041,6 +4159,7 @@ __name(runAgentLoop, "runAgentLoop");
4041
4159
  withMemoryTool,
4042
4160
  withSkillTool,
4043
4161
  withToolTimeout,
4162
+ withTurnFrames,
4044
4163
  writeMemory
4045
4164
  });
4046
4165
  //# sourceMappingURL=index.cjs.map