@dudousxd/nestjs-agent 1.37.0 → 1.38.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,5 +1,5 @@
1
1
  export * from '@dudousxd/nestjs-agent-core/a2ui';
2
- export { b as A2uiAdapterOptions, c as A2uiRunHandler, i as a2uiAdapter } from '../index-4caNqEQ-.cjs';
2
+ export { b as A2uiAdapterOptions, c as A2uiRunHandler, i as a2uiAdapter } from '../index-BJFM1tG8.cjs';
3
3
  import '@dudousxd/nestjs-agent-core';
4
4
  import 'express';
5
5
  import '@dudousxd/nestjs-agent-core/genui';
@@ -1,5 +1,5 @@
1
1
  export * from '@dudousxd/nestjs-agent-core/a2ui';
2
- export { b as A2uiAdapterOptions, c as A2uiRunHandler, i as a2uiAdapter } from '../index-yC_Ra3CE.js';
2
+ export { b as A2uiAdapterOptions, c as A2uiRunHandler, i as a2uiAdapter } from '../index-De98CGXa.js';
3
3
  import '@dudousxd/nestjs-agent-core';
4
4
  import 'express';
5
5
  import '@dudousxd/nestjs-agent-core/genui';
@@ -646,6 +646,18 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
646
646
  ];
647
647
  const hooks = {
648
648
  runId: ctx.runId,
649
+ // The runtime's own enqueue stamp: the run row's `createdAt` is written when the run is
650
+ // started (a send, a parent's `ctx.child`, the queue's `ctx.startChild`), so now − createdAt,
651
+ // read on the body's first execution (inside `persist:run:start`), is how long it waited for a
652
+ // worker. The clocks are the enqueuing process's and the worker's.
653
+ queueMs: /* @__PURE__ */ __name(async () => {
654
+ const runs = this.runs;
655
+ const run = typeof runs?.getRun === "function" ? await runs.getRun(ctx.runId) : (await runs?.getRunDetail(ctx.runId))?.run;
656
+ const createdAt = run?.createdAt;
657
+ if (createdAt === void 0) return void 0;
658
+ const at = new Date(createdAt).getTime();
659
+ return Number.isFinite(at) ? Date.now() - at : void 0;
660
+ }, "queueMs"),
649
661
  // A child forwards into the top-level sink but must not end/fail it (the top-level run owns it).
650
662
  // A thread's own turn hands the thread to its queue just before the loop ends the stream, so
651
663
  // the reader learns what runs next from the `queue` frame, before the end.
@@ -816,6 +828,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
816
828
  const timed = durationMs !== void 0 ? {
817
829
  durationMs
818
830
  } : {};
831
+ const measured = (0, import_nestjs_agent_core4.runEndTimings)(error);
819
832
  if ((0, import_nestjs_agent_core4.isReplayIntegrityError)(error)) {
820
833
  const detail = error.message;
821
834
  const code2 = (0, import_nestjs_agent_core4.agentFailureCode)(error);
@@ -826,6 +839,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
826
839
  code: code2,
827
840
  message: detail,
828
841
  ...timed,
842
+ ...measured,
829
843
  context: (0, import_nestjs_agent_core4.agentRunContext)(input)
830
844
  });
831
845
  await (0, import_nestjs_agent_core4.settleDeadRun)(this.store, {
@@ -849,6 +863,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
849
863
  code,
850
864
  message,
851
865
  ...timed,
866
+ ...measured,
852
867
  context: (0, import_nestjs_agent_core4.agentRunContext)(input)
853
868
  });
854
869
  await stepOf(ctx)("persist:run:fail", async () => {
@@ -3014,6 +3029,7 @@ function _ts_param7(paramIndex, decorator) {
3014
3029
  };
3015
3030
  }
3016
3031
  __name(_ts_param7, "_ts_param");
3032
+ var usageLogger = new import_common10.Logger("AgentService");
3017
3033
  var NO_USER_MESSAGE_CODE = "no_user_message";
3018
3034
  function sendableParams(params) {
3019
3035
  const message = params.message;
@@ -4257,6 +4273,31 @@ var AgentService = class {
4257
4273
  throw new import_common10.ForbiddenException("run belongs to another actor");
4258
4274
  }
4259
4275
  }
4276
+ /**
4277
+ * Record AI spend that did not come from an agent turn — a transcription, a TTS render, a batch of
4278
+ * embeddings, a direct OpenAI/OpenRouter SDK call — on the same ledger the agent's turns write, so
4279
+ * cost, quota and the dashboard see one figure.
4280
+ *
4281
+ * Never throws into the caller's flow by default: a failed write or an invalid input is logged and
4282
+ * answered with `{ recorded: false, error }`. Pass `{ throwOnError: true }` where losing the row
4283
+ * must fail the caller.
4284
+ *
4285
+ * Counted against `actorRef` like an agent turn: the ledger is the budget (`LedgerQuotaProvider`),
4286
+ * so its tokens count in the token windows (a row with no tokens adds 0 there) and its `costUsd` in
4287
+ * the USD windows. A `QuotaStore` counter is bumped only where loop deps carry one (`AgentModule`
4288
+ * wires none). `actorRef: null` is platform cost, recorded under `PLATFORM_ACTOR_REF` and counted
4289
+ * against nobody.
4290
+ */
4291
+ recordUsage(input, options = {}) {
4292
+ return (0, import_nestjs_agent_core16.recordAiUsage)({
4293
+ store: this.store,
4294
+ quota: this.deps.forAgent().quota,
4295
+ day: utcDay(),
4296
+ input,
4297
+ options,
4298
+ warn: /* @__PURE__ */ __name((message) => usageLogger.warn(message), "warn")
4299
+ });
4300
+ }
4260
4301
  };
4261
4302
  AgentService = _ts_decorate9([
4262
4303
  (0, import_common10.Injectable)(),
@@ -7237,10 +7278,12 @@ var InlineAgentRunner = class _InlineAgentRunner {
7237
7278
  }
7238
7279
  async start(input, options = {}) {
7239
7280
  const runId = options.runId ?? crypto.randomUUID();
7281
+ const acceptedAt = Date.now();
7240
7282
  const day = input.day ?? utcDay();
7241
7283
  const deps = this.factory.forAgent(input.agentName);
7242
7284
  this.live.add(runId);
7243
7285
  const hooks = this.topLevelHooks({
7286
+ acceptedAt,
7244
7287
  runId,
7245
7288
  deps,
7246
7289
  actor: input.actor,
@@ -7262,8 +7305,9 @@ var InlineAgentRunner = class _InlineAgentRunner {
7262
7305
  const timed = durationMs !== void 0 ? {
7263
7306
  durationMs
7264
7307
  } : {};
7308
+ const measured = (0, import_nestjs_agent_core38.runEndTimings)(error);
7265
7309
  if (error instanceof import_nestjs_agent_core38.RunCancelledError) {
7266
- await this.settleCancelled(runId, input, deps, durationMs);
7310
+ await this.settleCancelled(runId, input, deps, durationMs, measured);
7267
7311
  return;
7268
7312
  }
7269
7313
  const message = error instanceof Error ? error.message : String(error);
@@ -7275,6 +7319,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
7275
7319
  code,
7276
7320
  message,
7277
7321
  ...timed,
7322
+ ...measured,
7278
7323
  context: (0, import_nestjs_agent_core38.agentRunContext)(input)
7279
7324
  });
7280
7325
  await this.store.recordRunEnd?.({
@@ -7352,7 +7397,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
7352
7397
  * the stream ENDED after a `cancelled` frame rather than failed, so a client that retries failed
7353
7398
  * streams does not retry this one.
7354
7399
  */
7355
- async settleCancelled(runId, input, deps, durationMs) {
7400
+ async settleCancelled(runId, input, deps, durationMs, measured = {}) {
7356
7401
  this.logger.log(`agent run ${runId} cancelled`);
7357
7402
  const timed = durationMs !== void 0 ? {
7358
7403
  durationMs
@@ -7361,6 +7406,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
7361
7406
  runId,
7362
7407
  threadId: input.threadId,
7363
7408
  ...timed,
7409
+ ...measured,
7364
7410
  context: (0, import_nestjs_agent_core38.agentRunContext)(input)
7365
7411
  });
7366
7412
  const writer = await deps.sink.open(runId);
@@ -7453,9 +7499,10 @@ var InlineAgentRunner = class _InlineAgentRunner {
7453
7499
  });
7454
7500
  }
7455
7501
  topLevelHooks(args) {
7456
- const { runId, deps, actor, day, threadId, chainBelow, input } = args;
7502
+ const { runId, deps, actor, day, threadId, chainBelow, input, acceptedAt } = args;
7457
7503
  return {
7458
7504
  runId,
7505
+ queueMs: /* @__PURE__ */ __name(() => Date.now() - acceptedAt, "queueMs"),
7459
7506
  // The loop ends the stream when the turn completes. Just before, the thread passes to the next
7460
7507
  // queued message (or is released), and the reader is told which — so by the time a client
7461
7508
  // sees the run end, the thread is already free or already running what it queued.
@@ -7580,10 +7627,12 @@ var InlineAgentRunner = class _InlineAgentRunner {
7580
7627
  const timed = durationMs !== void 0 ? {
7581
7628
  durationMs
7582
7629
  } : {};
7630
+ const measured = (0, import_nestjs_agent_core38.runEndTimings)(error);
7583
7631
  const settled = {
7584
7632
  runId,
7585
7633
  threadId: subThread.id,
7586
7634
  ...timed,
7635
+ ...measured,
7587
7636
  context: (0, import_nestjs_agent_core38.agentRunContext)(childInput)
7588
7637
  };
7589
7638
  if (cancelled) {