@dudousxd/nestjs-agent 1.36.2 → 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-CJdTomOT.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-D1xRrnCq.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;
@@ -3841,9 +3857,28 @@ var AgentService = class {
3841
3857
  await this.assertOwnsThread(actor, threadId);
3842
3858
  return this.store.softDeleteThread(threadId);
3843
3859
  }
3844
- async forkThread(actor, threadId, fromMessageId) {
3845
- await this.assertOwnsThread(actor, threadId);
3846
- return this.store.forkThread(threadId, fromMessageId);
3860
+ /**
3861
+ * Copy a thread up to `fromMessageId` (inclusive) into a new one. By default only the thread's
3862
+ * owner may, and the copy is theirs. With `params.authorized`, the host has vouched for `actor`
3863
+ * (a share link it checked) and the copy is `actor`'s — see {@link ForkThreadParams}.
3864
+ */
3865
+ async forkThread(actor, threadId, fromMessageId, params = {}) {
3866
+ if (params.authorized === true) {
3867
+ if (await this.store.ownerOfThread(threadId) === null) {
3868
+ throw new import_common10.NotFoundException(`thread ${threadId} not found`);
3869
+ }
3870
+ } else {
3871
+ await this.assertOwnsThread(actor, threadId);
3872
+ }
3873
+ const options = {
3874
+ ...params.authorized === true ? {
3875
+ actorRef: actor.id
3876
+ } : {},
3877
+ ...params.tenantRef !== void 0 ? {
3878
+ tenantRef: params.tenantRef
3879
+ } : {}
3880
+ };
3881
+ return this.store.forkThread(threadId, fromMessageId, options);
3847
3882
  }
3848
3883
  /** Kept for source compatibility — a thin wrapper over the more general `updateThread`. */
3849
3884
  async renameThread(actor, threadId, title) {
@@ -4238,6 +4273,31 @@ var AgentService = class {
4238
4273
  throw new import_common10.ForbiddenException("run belongs to another actor");
4239
4274
  }
4240
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
+ }
4241
4301
  };
4242
4302
  AgentService = _ts_decorate9([
4243
4303
  (0, import_common10.Injectable)(),
@@ -7218,10 +7278,12 @@ var InlineAgentRunner = class _InlineAgentRunner {
7218
7278
  }
7219
7279
  async start(input, options = {}) {
7220
7280
  const runId = options.runId ?? crypto.randomUUID();
7281
+ const acceptedAt = Date.now();
7221
7282
  const day = input.day ?? utcDay();
7222
7283
  const deps = this.factory.forAgent(input.agentName);
7223
7284
  this.live.add(runId);
7224
7285
  const hooks = this.topLevelHooks({
7286
+ acceptedAt,
7225
7287
  runId,
7226
7288
  deps,
7227
7289
  actor: input.actor,
@@ -7243,8 +7305,9 @@ var InlineAgentRunner = class _InlineAgentRunner {
7243
7305
  const timed = durationMs !== void 0 ? {
7244
7306
  durationMs
7245
7307
  } : {};
7308
+ const measured = (0, import_nestjs_agent_core38.runEndTimings)(error);
7246
7309
  if (error instanceof import_nestjs_agent_core38.RunCancelledError) {
7247
- await this.settleCancelled(runId, input, deps, durationMs);
7310
+ await this.settleCancelled(runId, input, deps, durationMs, measured);
7248
7311
  return;
7249
7312
  }
7250
7313
  const message = error instanceof Error ? error.message : String(error);
@@ -7256,6 +7319,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
7256
7319
  code,
7257
7320
  message,
7258
7321
  ...timed,
7322
+ ...measured,
7259
7323
  context: (0, import_nestjs_agent_core38.agentRunContext)(input)
7260
7324
  });
7261
7325
  await this.store.recordRunEnd?.({
@@ -7333,7 +7397,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
7333
7397
  * the stream ENDED after a `cancelled` frame rather than failed, so a client that retries failed
7334
7398
  * streams does not retry this one.
7335
7399
  */
7336
- async settleCancelled(runId, input, deps, durationMs) {
7400
+ async settleCancelled(runId, input, deps, durationMs, measured = {}) {
7337
7401
  this.logger.log(`agent run ${runId} cancelled`);
7338
7402
  const timed = durationMs !== void 0 ? {
7339
7403
  durationMs
@@ -7342,6 +7406,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
7342
7406
  runId,
7343
7407
  threadId: input.threadId,
7344
7408
  ...timed,
7409
+ ...measured,
7345
7410
  context: (0, import_nestjs_agent_core38.agentRunContext)(input)
7346
7411
  });
7347
7412
  const writer = await deps.sink.open(runId);
@@ -7434,9 +7499,10 @@ var InlineAgentRunner = class _InlineAgentRunner {
7434
7499
  });
7435
7500
  }
7436
7501
  topLevelHooks(args) {
7437
- const { runId, deps, actor, day, threadId, chainBelow, input } = args;
7502
+ const { runId, deps, actor, day, threadId, chainBelow, input, acceptedAt } = args;
7438
7503
  return {
7439
7504
  runId,
7505
+ queueMs: /* @__PURE__ */ __name(() => Date.now() - acceptedAt, "queueMs"),
7440
7506
  // The loop ends the stream when the turn completes. Just before, the thread passes to the next
7441
7507
  // queued message (or is released), and the reader is told which — so by the time a client
7442
7508
  // sees the run end, the thread is already free or already running what it queued.
@@ -7561,10 +7627,12 @@ var InlineAgentRunner = class _InlineAgentRunner {
7561
7627
  const timed = durationMs !== void 0 ? {
7562
7628
  durationMs
7563
7629
  } : {};
7630
+ const measured = (0, import_nestjs_agent_core38.runEndTimings)(error);
7564
7631
  const settled = {
7565
7632
  runId,
7566
7633
  threadId: subThread.id,
7567
7634
  ...timed,
7635
+ ...measured,
7568
7636
  context: (0, import_nestjs_agent_core38.agentRunContext)(childInput)
7569
7637
  };
7570
7638
  if (cancelled) {