@dudousxd/nestjs-agent 1.7.0 → 1.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.
package/dist/index.cjs CHANGED
@@ -40,6 +40,7 @@ __export(index_exports, {
40
40
  HeaderActorResolver: () => HeaderActorResolver,
41
41
  InProcessTokenStreamSink: () => InProcessTokenStreamSink,
42
42
  InlineAgentRunner: () => InlineAgentRunner,
43
+ LedgerQuotaProvider: () => LedgerQuotaProvider,
43
44
  LedgerQuotaStore: () => LedgerQuotaStore,
44
45
  MemoriesController: () => MemoriesController,
45
46
  MessagesController: () => MessagesController,
@@ -66,7 +67,7 @@ __export(index_exports, {
66
67
  module.exports = __toCommonJS(index_exports);
67
68
 
68
69
  // src/agent.module.ts
69
- var import_nestjs_agent_core22 = require("@dudousxd/nestjs-agent-core");
70
+ var import_nestjs_agent_core23 = require("@dudousxd/nestjs-agent-core");
70
71
  var import_common19 = require("@nestjs/common");
71
72
  var import_core4 = require("@nestjs/core");
72
73
 
@@ -392,13 +393,68 @@ var AgentService = class {
392
393
  quota;
393
394
  staging;
394
395
  models;
395
- constructor(runner, store, deps, quota, staging, models) {
396
+ quotaProvider;
397
+ options;
398
+ constructor(runner, store, deps, quota, staging, models, quotaProvider, options) {
396
399
  this.runner = runner;
397
400
  this.store = store;
398
401
  this.deps = deps;
399
402
  this.quota = quota;
400
403
  this.staging = staging;
401
404
  this.models = models;
405
+ this.quotaProvider = quotaProvider;
406
+ this.options = options;
407
+ }
408
+ /**
409
+ * The actor's budget across windows (`GET <base>/quota`) — the bound {@link QuotaProvider}'s
410
+ * report. Without one (a service built outside the module), a day window from the ledger.
411
+ */
412
+ async quotaReport(actor) {
413
+ if (this.quotaProvider !== void 0) {
414
+ return this.quotaProvider.report({
415
+ actor
416
+ });
417
+ }
418
+ const today = await this.quotaToday(actor.id);
419
+ return {
420
+ windows: [
421
+ {
422
+ period: "day",
423
+ usedTokens: today.usedTokens,
424
+ ...today.limitTokens !== null ? {
425
+ limitTokens: today.limitTokens
426
+ } : {},
427
+ usedUsd: today.costUsd
428
+ }
429
+ ],
430
+ ...today.withinLimit ? {} : {
431
+ blocked: {
432
+ period: "day"
433
+ }
434
+ }
435
+ };
436
+ }
437
+ /**
438
+ * Refuse a turn the actor's budget no longer covers — only when the host configured one
439
+ * (`quotaProvider` or `quotaLimits`): the default report is informational, and the daily token
440
+ * quota the loop enforces keeps working as it always has.
441
+ */
442
+ async assertWithinQuota(actor) {
443
+ const gated = this.options?.quotaProvider !== void 0 || this.options?.quotaLimits !== void 0;
444
+ if (!gated || this.quotaProvider === void 0) {
445
+ return;
446
+ }
447
+ const { blocked } = await this.quotaProvider.report({
448
+ actor
449
+ });
450
+ if (blocked !== void 0) {
451
+ throw new import_common2.HttpException({
452
+ statusCode: import_common2.HttpStatus.TOO_MANY_REQUESTS,
453
+ code: "quota_exceeded",
454
+ period: blocked.period,
455
+ message: blocked.reason ?? `The ${blocked.period === "day" ? "daily" : "monthly"} quota is used up`
456
+ }, import_common2.HttpStatus.TOO_MANY_REQUESTS);
457
+ }
402
458
  }
403
459
  /**
404
460
  * The models `actor` may pick for `agent` (`GET <base>/models`). An empty catalog when none is
@@ -455,6 +511,7 @@ var AgentService = class {
455
511
  return (await this.store.getThread(threadId))?.model ?? null;
456
512
  }
457
513
  async chat(params) {
514
+ await this.assertWithinQuota(params.actor);
458
515
  const agentName = await this.resolveAgentName(params.agentName, params.threadId);
459
516
  const model = await this.resolveModel(params.actor, agentName, params.model, params.threadId);
460
517
  const attachments = await this.resolveAttachments(params.actor, params.attachments ?? []);
@@ -996,6 +1053,10 @@ AgentService = _ts_decorate2([
996
1053
  _ts_param2(4, (0, import_common2.Inject)(import_nestjs_agent_core3.AGENT_ATTACHMENT_STAGING)),
997
1054
  _ts_param2(5, (0, import_common2.Optional)()),
998
1055
  _ts_param2(5, (0, import_common2.Inject)(import_nestjs_agent_core3.AGENT_MODEL_CATALOG)),
1056
+ _ts_param2(6, (0, import_common2.Optional)()),
1057
+ _ts_param2(6, (0, import_common2.Inject)(import_nestjs_agent_core3.AGENT_QUOTA_PROVIDER)),
1058
+ _ts_param2(7, (0, import_common2.Optional)()),
1059
+ _ts_param2(7, (0, import_common2.Inject)(import_nestjs_agent_core3.AGENT_OPTIONS)),
999
1060
  _ts_metadata2("design:type", Function),
1000
1061
  _ts_metadata2("design:paramtypes", [
1001
1062
  typeof AgentRunner === "undefined" ? Object : AgentRunner,
@@ -1003,7 +1064,9 @@ AgentService = _ts_decorate2([
1003
1064
  typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
1004
1065
  Object,
1005
1066
  typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore,
1006
- typeof ModelCatalog === "undefined" ? Object : ModelCatalog
1067
+ typeof ModelCatalog === "undefined" ? Object : ModelCatalog,
1068
+ typeof QuotaProvider === "undefined" ? Object : QuotaProvider,
1069
+ typeof Pick === "undefined" ? Object : Pick
1007
1070
  ])
1008
1071
  ], AgentService);
1009
1072
 
@@ -1850,11 +1913,28 @@ var QuotaController = class {
1850
1913
  this.agent = agent;
1851
1914
  this.actorResolver = actorResolver;
1852
1915
  }
1916
+ /**
1917
+ * The caller's budget across windows — `{ windows: [{ period, usedTokens, limitTokens?, usedUsd,
1918
+ * limitUsd?, resetsAt? }], blocked?: { period, reason? } }` — from the bound `QuotaProvider`.
1919
+ */
1920
+ async report(req) {
1921
+ const actor = await this.actorResolver.resolve(req);
1922
+ return this.agent.quotaReport(actor);
1923
+ }
1853
1924
  async today(req) {
1854
1925
  const actor = await this.actorResolver.resolve(req);
1855
1926
  return this.agent.quotaToday(actor.id);
1856
1927
  }
1857
1928
  };
1929
+ _ts_decorate10([
1930
+ (0, import_common10.Get)(),
1931
+ _ts_param9(0, (0, import_common10.Req)()),
1932
+ _ts_metadata10("design:type", Function),
1933
+ _ts_metadata10("design:paramtypes", [
1934
+ typeof Request === "undefined" ? Object : Request
1935
+ ]),
1936
+ _ts_metadata10("design:returntype", Promise)
1937
+ ], QuotaController.prototype, "report", null);
1858
1938
  _ts_decorate10([
1859
1939
  (0, import_common10.Get)("today"),
1860
1940
  _ts_param9(0, (0, import_common10.Req)()),
@@ -3023,6 +3103,69 @@ var InProcessTokenStreamSink = class {
3023
3103
  }
3024
3104
  };
3025
3105
 
3106
+ // src/ledger-quota-provider.ts
3107
+ var import_nestjs_agent_core20 = require("@dudousxd/nestjs-agent-core");
3108
+ var LedgerQuotaProvider = class {
3109
+ static {
3110
+ __name(this, "LedgerQuotaProvider");
3111
+ }
3112
+ store;
3113
+ quota;
3114
+ limits;
3115
+ constructor(store, quota, limits = {}) {
3116
+ this.store = store;
3117
+ this.quota = quota;
3118
+ this.limits = limits;
3119
+ }
3120
+ async report(query) {
3121
+ const now = query.now ?? /* @__PURE__ */ new Date();
3122
+ const actorRef = query.actor.id;
3123
+ const windows = [
3124
+ await this.dayWindow(actorRef, now)
3125
+ ];
3126
+ if (this.store.usageBetween !== void 0) {
3127
+ const range = (0, import_nestjs_agent_core20.quotaPeriodRange)("month", now);
3128
+ const used = await this.store.usageBetween(actorRef, range.fromDay, range.toDay);
3129
+ windows.push(this.window("month", used.usedTokens, used.costUsd, range.resetsAt));
3130
+ }
3131
+ const blocked = (0, import_nestjs_agent_core20.exhaustedWindow)(windows);
3132
+ return {
3133
+ windows,
3134
+ ...blocked !== void 0 ? {
3135
+ blocked
3136
+ } : {}
3137
+ };
3138
+ }
3139
+ async dayWindow(actorRef, now) {
3140
+ const range = (0, import_nestjs_agent_core20.quotaPeriodRange)("day", now);
3141
+ const { usedTokens, costUsd } = await this.store.quotaToday(actorRef, range.fromDay);
3142
+ if (this.quota === void 0) {
3143
+ return this.window("day", usedTokens, costUsd, range.resetsAt);
3144
+ }
3145
+ const state = await this.quota.check(actorRef, range.fromDay);
3146
+ const window = this.window("day", state.usedTokens, costUsd, range.resetsAt);
3147
+ if (window.limitTokens === void 0) {
3148
+ window.limitTokens = state.limitTokens;
3149
+ }
3150
+ return window;
3151
+ }
3152
+ window(period, usedTokens, usedUsd, resetsAt) {
3153
+ const limits = this.limits[period] ?? {};
3154
+ return {
3155
+ period,
3156
+ usedTokens,
3157
+ ...limits.tokens !== void 0 ? {
3158
+ limitTokens: limits.tokens
3159
+ } : {},
3160
+ usedUsd,
3161
+ ...limits.usd !== void 0 ? {
3162
+ limitUsd: limits.usd
3163
+ } : {},
3164
+ resetsAt
3165
+ };
3166
+ }
3167
+ };
3168
+
3026
3169
  // src/ledger-quota-store.ts
3027
3170
  var LedgerQuotaStore = class {
3028
3171
  static {
@@ -3047,7 +3190,7 @@ var LedgerQuotaStore = class {
3047
3190
  };
3048
3191
 
3049
3192
  // src/runner/inline-agent-runner.ts
3050
- var import_nestjs_agent_core20 = require("@dudousxd/nestjs-agent-core");
3193
+ var import_nestjs_agent_core21 = require("@dudousxd/nestjs-agent-core");
3051
3194
  var import_common18 = require("@nestjs/common");
3052
3195
  function _ts_decorate18(decorators, target, key, desc) {
3053
3196
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -3110,18 +3253,18 @@ var InlineAgentRunner = class _InlineAgentRunner {
3110
3253
  ] : []
3111
3254
  ]
3112
3255
  });
3113
- void (0, import_nestjs_agent_core20.runAgentLoop)({
3256
+ void (0, import_nestjs_agent_core21.runAgentLoop)({
3114
3257
  ...deps,
3115
3258
  day
3116
3259
  }, input, hooks).then(() => this.store.setActiveStream(input.threadId, null)).catch(async (error) => {
3117
- if (error instanceof import_nestjs_agent_core20.RunCancelledError) {
3260
+ if (error instanceof import_nestjs_agent_core21.RunCancelledError) {
3118
3261
  await this.settleCancelled(runId, input.threadId, deps);
3119
3262
  return;
3120
3263
  }
3121
3264
  const message = error instanceof Error ? error.message : String(error);
3122
- const code = (0, import_nestjs_agent_core20.agentFailureCode)(error);
3265
+ const code = (0, import_nestjs_agent_core21.agentFailureCode)(error);
3123
3266
  this.logger.error(`agent run ${runId} failed (${code}): ${message}`);
3124
- (0, import_nestjs_agent_core20.publishAgentRunFailed)({
3267
+ (0, import_nestjs_agent_core21.publishAgentRunFailed)({
3125
3268
  runId,
3126
3269
  code,
3127
3270
  message
@@ -3170,7 +3313,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
3170
3313
  for (const [key, waiter] of this.pending) {
3171
3314
  if (key.startsWith(`${runId}:`)) {
3172
3315
  this.pending.delete(key);
3173
- waiter.reject(new import_nestjs_agent_core20.RunCancelledError());
3316
+ waiter.reject(new import_nestjs_agent_core21.RunCancelledError());
3174
3317
  }
3175
3318
  }
3176
3319
  await Promise.resolve();
@@ -3185,7 +3328,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
3185
3328
  this.logger.log(`agent run ${runId} cancelled`);
3186
3329
  await this.store.setActiveStream(threadId, null);
3187
3330
  const writer = await deps.sink.open(runId);
3188
- await writer.write((0, import_nestjs_agent_core20.encodeStreamEvent)({
3331
+ await writer.write((0, import_nestjs_agent_core21.encodeStreamEvent)({
3189
3332
  kind: "cancelled"
3190
3333
  }));
3191
3334
  await writer.end();
@@ -3246,7 +3389,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
3246
3389
  awaitApproval: /* @__PURE__ */ __name((call, _ctx, opts) => this.parkApproval(runId, call.id, opts?.timeoutMs), "awaitApproval"),
3247
3390
  awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
3248
3391
  step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
3249
- parallel: import_nestjs_agent_core20.settleAll,
3392
+ parallel: import_nestjs_agent_core21.settleAll,
3250
3393
  cancelled: /* @__PURE__ */ __name(async () => this.cancelled.has(runId), "cancelled"),
3251
3394
  runAgent: /* @__PURE__ */ __name((agentName, task) => this.runNested({
3252
3395
  agentName,
@@ -3297,7 +3440,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
3297
3440
  awaitApproval: /* @__PURE__ */ __name((call, _ctx, opts) => this.parkApproval(runId, call.id, opts?.timeoutMs), "awaitApproval"),
3298
3441
  awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
3299
3442
  step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
3300
- parallel: import_nestjs_agent_core20.settleAll,
3443
+ parallel: import_nestjs_agent_core21.settleAll,
3301
3444
  cancelled: /* @__PURE__ */ __name(async () => this.cancelled.has(runId), "cancelled"),
3302
3445
  runAgent: /* @__PURE__ */ __name((childName, childTask) => this.runNested({
3303
3446
  agentName: childName,
@@ -3330,7 +3473,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
3330
3473
  }
3331
3474
  }), "startAgent")
3332
3475
  };
3333
- void (0, import_nestjs_agent_core20.runAgentLoop)({
3476
+ void (0, import_nestjs_agent_core21.runAgentLoop)({
3334
3477
  ...deps,
3335
3478
  day
3336
3479
  }, {
@@ -3344,13 +3487,13 @@ var InlineAgentRunner = class _InlineAgentRunner {
3344
3487
  parentRunId,
3345
3488
  deliverTo
3346
3489
  }, hooks).catch(async (error) => {
3347
- const cancelled = error instanceof import_nestjs_agent_core20.RunCancelledError;
3490
+ const cancelled = error instanceof import_nestjs_agent_core21.RunCancelledError;
3348
3491
  const message = error instanceof Error ? error.message : String(error);
3349
3492
  if (!cancelled) {
3350
3493
  this.logger.error(`detached agent run ${runId} failed: ${message}`);
3351
- (0, import_nestjs_agent_core20.publishAgentRunFailed)({
3494
+ (0, import_nestjs_agent_core21.publishAgentRunFailed)({
3352
3495
  runId,
3353
- code: (0, import_nestjs_agent_core20.agentFailureCode)(error),
3496
+ code: (0, import_nestjs_agent_core21.agentFailureCode)(error),
3354
3497
  message
3355
3498
  });
3356
3499
  }
@@ -3358,11 +3501,11 @@ var InlineAgentRunner = class _InlineAgentRunner {
3358
3501
  runId,
3359
3502
  status: cancelled ? "cancelled" : "failed",
3360
3503
  ...cancelled ? {} : {
3361
- errorCode: (0, import_nestjs_agent_core20.agentFailureCode)(error),
3504
+ errorCode: (0, import_nestjs_agent_core21.agentFailureCode)(error),
3362
3505
  errorMessage: message
3363
3506
  }
3364
3507
  });
3365
- await (0, import_nestjs_agent_core20.settleUnsettledDelegation)({
3508
+ await (0, import_nestjs_agent_core21.settleUnsettledDelegation)({
3366
3509
  store: this.store,
3367
3510
  delivery: deliverTo,
3368
3511
  agent: agentName,
@@ -3401,7 +3544,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
3401
3544
  awaitApproval: /* @__PURE__ */ __name((call, _ctx, opts) => this.parkApproval(runId, call.id, opts?.timeoutMs), "awaitApproval"),
3402
3545
  awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
3403
3546
  step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
3404
- parallel: import_nestjs_agent_core20.settleAll,
3547
+ parallel: import_nestjs_agent_core21.settleAll,
3405
3548
  // A child stops when it is cancelled OR when the run a human is actually watching is: the
3406
3549
  // canceller names the top-level run, which is the only id that ever left the server.
3407
3550
  cancelled: /* @__PURE__ */ __name(async () => this.cancelled.has(runId) || this.cancelled.has(sinkRunId), "cancelled"),
@@ -3436,7 +3579,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
3436
3579
  }), "startAgent")
3437
3580
  };
3438
3581
  try {
3439
- return await (0, import_nestjs_agent_core20.runAgentLoop)({
3582
+ return await (0, import_nestjs_agent_core21.runAgentLoop)({
3440
3583
  ...deps,
3441
3584
  day
3442
3585
  }, {
@@ -3457,8 +3600,8 @@ var InlineAgentRunner = class _InlineAgentRunner {
3457
3600
  };
3458
3601
  InlineAgentRunner = _ts_decorate18([
3459
3602
  (0, import_common18.Injectable)(),
3460
- _ts_param17(0, (0, import_common18.Inject)(import_nestjs_agent_core20.AGENT_DEPS_FACTORY)),
3461
- _ts_param17(1, (0, import_common18.Inject)(import_nestjs_agent_core20.AGENT_STORE)),
3603
+ _ts_param17(0, (0, import_common18.Inject)(import_nestjs_agent_core21.AGENT_DEPS_FACTORY)),
3604
+ _ts_param17(1, (0, import_common18.Inject)(import_nestjs_agent_core21.AGENT_STORE)),
3462
3605
  _ts_metadata18("design:type", Function),
3463
3606
  _ts_metadata18("design:paramtypes", [
3464
3607
  typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
@@ -3467,7 +3610,7 @@ InlineAgentRunner = _ts_decorate18([
3467
3610
  ], InlineAgentRunner);
3468
3611
 
3469
3612
  // src/skills-config.ts
3470
- var import_nestjs_agent_core21 = require("@dudousxd/nestjs-agent-core");
3613
+ var import_nestjs_agent_core22 = require("@dudousxd/nestjs-agent-core");
3471
3614
  function declaredSkillProvider(declared) {
3472
3615
  return {
3473
3616
  list: /* @__PURE__ */ __name(({ scopes }) => declared.filter((skill) => scopes.includes(skill.scope)).map(({ name, description, scope }) => ({
@@ -3493,7 +3636,7 @@ function resolveSkillsConfig(options, declared) {
3493
3636
  declaredSkillProvider(declared)
3494
3637
  ];
3495
3638
  return {
3496
- provider: providers.length === 1 ? providers[0] : (0, import_nestjs_agent_core21.compositeSkillProvider)(providers),
3639
+ provider: providers.length === 1 ? providers[0] : (0, import_nestjs_agent_core22.compositeSkillProvider)(providers),
3497
3640
  ...options.scopes !== void 0 ? {
3498
3641
  scopes: options.scopes
3499
3642
  } : {},
@@ -3523,94 +3666,103 @@ var DEFAULT_PATH = "agent";
3523
3666
  function sharedProviders(durable, includeStore) {
3524
3667
  const providers = [
3525
3668
  {
3526
- provide: import_nestjs_agent_core22.AGENT_TOOL_REGISTRY,
3527
- useFactory: /* @__PURE__ */ __name(() => new import_nestjs_agent_core22.ToolRegistry(), "useFactory")
3669
+ provide: import_nestjs_agent_core23.AGENT_TOOL_REGISTRY,
3670
+ useFactory: /* @__PURE__ */ __name(() => new import_nestjs_agent_core23.ToolRegistry(), "useFactory")
3528
3671
  },
3529
3672
  // Starts empty; AgentDiscoveryService populates it from `@Agent`-decorated providers at boot.
3530
3673
  {
3531
- provide: import_nestjs_agent_core22.AGENT_REGISTRY,
3532
- useFactory: /* @__PURE__ */ __name(() => new import_nestjs_agent_core22.AgentRegistry(), "useFactory")
3674
+ provide: import_nestjs_agent_core23.AGENT_REGISTRY,
3675
+ useFactory: /* @__PURE__ */ __name(() => new import_nestjs_agent_core23.AgentRegistry(), "useFactory")
3533
3676
  },
3534
3677
  // A shared, mutable list AgentDiscoveryService fills with `@SystemPromptContributor()` methods.
3535
3678
  {
3536
- provide: import_nestjs_agent_core22.AGENT_PROMPT_CONTRIBUTORS,
3679
+ provide: import_nestjs_agent_core23.AGENT_PROMPT_CONTRIBUTORS,
3537
3680
  useFactory: /* @__PURE__ */ __name(() => [], "useFactory")
3538
3681
  },
3539
3682
  // Likewise for `@Skill`-decorated providers — filled by SkillDiscoveryService at onModuleInit,
3540
3683
  // read lazily by the AGENT_SKILLS provider below (which DI builds before discovery has run).
3541
3684
  {
3542
- provide: import_nestjs_agent_core22.AGENT_SKILL_SOURCES,
3685
+ provide: import_nestjs_agent_core23.AGENT_SKILL_SOURCES,
3543
3686
  useFactory: /* @__PURE__ */ __name(() => [], "useFactory")
3544
3687
  },
3545
3688
  {
3546
3689
  // One resolution, shared by the loop's deps and the listing endpoint: a second one would
3547
3690
  // drift, and a user would be offered a skill the agent cannot reach. `undefined` when the
3548
3691
  // host configured none — which is what keeps the turn's checkpoint sequence unchanged.
3549
- provide: import_nestjs_agent_core22.AGENT_SKILLS,
3692
+ provide: import_nestjs_agent_core23.AGENT_SKILLS,
3550
3693
  useFactory: /* @__PURE__ */ __name((options, declared) => resolveSkillsConfig(options.skills, declared), "useFactory"),
3551
3694
  inject: [
3552
- import_nestjs_agent_core22.AGENT_OPTIONS,
3553
- import_nestjs_agent_core22.AGENT_SKILL_SOURCES
3695
+ import_nestjs_agent_core23.AGENT_OPTIONS,
3696
+ import_nestjs_agent_core23.AGENT_SKILL_SOURCES
3554
3697
  ]
3555
3698
  },
3556
3699
  {
3557
3700
  // One resolution, shared by the loop's deps and the read-back endpoint: a person has to be
3558
3701
  // shown what the model was shown, and two resolutions would eventually disagree about that.
3559
- provide: import_nestjs_agent_core22.AGENT_MEMORY,
3702
+ provide: import_nestjs_agent_core23.AGENT_MEMORY,
3560
3703
  useFactory: /* @__PURE__ */ __name((options) => options.memory, "useFactory"),
3561
3704
  inject: [
3562
- import_nestjs_agent_core22.AGENT_OPTIONS
3705
+ import_nestjs_agent_core23.AGENT_OPTIONS
3563
3706
  ]
3564
3707
  },
3565
3708
  {
3566
- provide: import_nestjs_agent_core22.AGENT_SINK,
3709
+ provide: import_nestjs_agent_core23.AGENT_SINK,
3567
3710
  useFactory: /* @__PURE__ */ __name((options) => options.sink ?? new InProcessTokenStreamSink(), "useFactory"),
3568
3711
  inject: [
3569
- import_nestjs_agent_core22.AGENT_OPTIONS
3712
+ import_nestjs_agent_core23.AGENT_OPTIONS
3570
3713
  ]
3571
3714
  },
3572
3715
  {
3573
- provide: import_nestjs_agent_core22.AGENT_ROLES_POLICY,
3574
- useFactory: /* @__PURE__ */ __name((options) => options.rolesPolicy ?? new import_nestjs_agent_core22.DefaultRolesPolicy(options.defaultRoles), "useFactory"),
3716
+ provide: import_nestjs_agent_core23.AGENT_ROLES_POLICY,
3717
+ useFactory: /* @__PURE__ */ __name((options) => options.rolesPolicy ?? new import_nestjs_agent_core23.DefaultRolesPolicy(options.defaultRoles), "useFactory"),
3575
3718
  inject: [
3576
- import_nestjs_agent_core22.AGENT_OPTIONS
3719
+ import_nestjs_agent_core23.AGENT_OPTIONS
3577
3720
  ]
3578
3721
  },
3579
3722
  {
3580
- provide: import_nestjs_agent_core22.AGENT_ACTOR_RESOLVER,
3723
+ provide: import_nestjs_agent_core23.AGENT_ACTOR_RESOLVER,
3581
3724
  useFactory: /* @__PURE__ */ __name((options) => options.actorResolver, "useFactory"),
3582
3725
  inject: [
3583
- import_nestjs_agent_core22.AGENT_OPTIONS
3726
+ import_nestjs_agent_core23.AGENT_OPTIONS
3584
3727
  ]
3585
3728
  },
3586
3729
  {
3587
- provide: import_nestjs_agent_core22.AGENT_MODEL,
3730
+ provide: import_nestjs_agent_core23.AGENT_MODEL,
3588
3731
  useFactory: /* @__PURE__ */ __name((o) => o.model, "useFactory"),
3589
3732
  inject: [
3590
- import_nestjs_agent_core22.AGENT_OPTIONS
3733
+ import_nestjs_agent_core23.AGENT_OPTIONS
3591
3734
  ]
3592
3735
  },
3593
3736
  {
3594
3737
  // An explicit `quota` wins; otherwise `quotaLimitTokens` binds the built-in ledger-backed
3595
3738
  // store; otherwise quotas are off (undefined). The ledger store reads the same usage rows
3596
3739
  // the loop already writes, so enforcement needs no extra shared state across replicas.
3597
- provide: import_nestjs_agent_core22.AGENT_QUOTA_STORE,
3740
+ provide: import_nestjs_agent_core23.AGENT_QUOTA_STORE,
3598
3741
  useFactory: /* @__PURE__ */ __name((o, store) => o.quota ?? (o.quotaLimitTokens !== void 0 ? new LedgerQuotaStore(store, o.quotaLimitTokens) : void 0), "useFactory"),
3599
3742
  inject: [
3600
- import_nestjs_agent_core22.AGENT_OPTIONS,
3601
- import_nestjs_agent_core22.AGENT_STORE
3743
+ import_nestjs_agent_core23.AGENT_OPTIONS,
3744
+ import_nestjs_agent_core23.AGENT_STORE
3745
+ ]
3746
+ },
3747
+ {
3748
+ provide: import_nestjs_agent_core23.AGENT_QUOTA_PROVIDER,
3749
+ useFactory: /* @__PURE__ */ __name((o, store, quota) => o.quotaProvider ?? new LedgerQuotaProvider(store, quota, o.quotaLimits), "useFactory"),
3750
+ inject: [
3751
+ import_nestjs_agent_core23.AGENT_OPTIONS,
3752
+ import_nestjs_agent_core23.AGENT_STORE,
3753
+ import_nestjs_agent_core23.AGENT_QUOTA_STORE
3602
3754
  ]
3603
3755
  },
3604
3756
  {
3605
- provide: import_nestjs_agent_core22.AGENT_MODEL_CATALOG,
3757
+ provide: import_nestjs_agent_core23.AGENT_MODEL_CATALOG,
3606
3758
  useFactory: /* @__PURE__ */ __name((o) => o.models, "useFactory"),
3607
3759
  inject: [
3608
- import_nestjs_agent_core22.AGENT_OPTIONS
3760
+ import_nestjs_agent_core23.AGENT_OPTIONS
3609
3761
  ]
3610
3762
  },
3611
3763
  AgentDepsFactory2,
3612
3764
  {
3613
- provide: import_nestjs_agent_core22.AGENT_DEPS_FACTORY,
3765
+ provide: import_nestjs_agent_core23.AGENT_DEPS_FACTORY,
3614
3766
  useExisting: AgentDepsFactory2
3615
3767
  },
3616
3768
  // Populates the registry + contributors (onModuleInit) BEFORE AiToolDiscoveryService synthesizes
@@ -3624,22 +3776,22 @@ function sharedProviders(durable, includeStore) {
3624
3776
  // through AgentService.signalToolCall, which reaches whichever AGENT_RUNNER is bound above.
3625
3777
  AgentApprovalPortAdapter,
3626
3778
  {
3627
- provide: import_nestjs_agent_core22.AGENT_APPROVAL_PORT,
3779
+ provide: import_nestjs_agent_core23.AGENT_APPROVAL_PORT,
3628
3780
  useExisting: AgentApprovalPortAdapter
3629
3781
  }
3630
3782
  ];
3631
3783
  if (includeStore) {
3632
3784
  providers.push({
3633
- provide: import_nestjs_agent_core22.AGENT_STORE,
3785
+ provide: import_nestjs_agent_core23.AGENT_STORE,
3634
3786
  useFactory: /* @__PURE__ */ __name((o) => o.store, "useFactory"),
3635
3787
  inject: [
3636
- import_nestjs_agent_core22.AGENT_OPTIONS
3788
+ import_nestjs_agent_core23.AGENT_OPTIONS
3637
3789
  ]
3638
3790
  });
3639
3791
  }
3640
3792
  if (durable) {
3641
3793
  providers.push({
3642
- provide: import_nestjs_agent_core22.AGENT_RUNNER,
3794
+ provide: import_nestjs_agent_core23.AGENT_RUNNER,
3643
3795
  useFactory: /* @__PURE__ */ __name((durableRunner) => {
3644
3796
  if (durableRunner === void 0) {
3645
3797
  throw new Error("AgentModule.forRoot({ durable: true }) requires importing AgentDurableModule from '@dudousxd/nestjs-agent/durable' (alongside a configured DurableModule). It was not found.");
@@ -3648,14 +3800,14 @@ function sharedProviders(durable, includeStore) {
3648
3800
  }, "useFactory"),
3649
3801
  inject: [
3650
3802
  {
3651
- token: import_nestjs_agent_core22.AGENT_DURABLE_RUNNER,
3803
+ token: import_nestjs_agent_core23.AGENT_DURABLE_RUNNER,
3652
3804
  optional: true
3653
3805
  }
3654
3806
  ]
3655
3807
  });
3656
3808
  } else {
3657
3809
  providers.push({
3658
- provide: import_nestjs_agent_core22.AGENT_RUNNER,
3810
+ provide: import_nestjs_agent_core23.AGENT_RUNNER,
3659
3811
  useExisting: InlineAgentRunner
3660
3812
  });
3661
3813
  }
@@ -3664,26 +3816,27 @@ function sharedProviders(durable, includeStore) {
3664
3816
  __name(sharedProviders, "sharedProviders");
3665
3817
  function exportsFor(includeStore) {
3666
3818
  return [
3667
- import_nestjs_agent_core22.AGENT_OPTIONS,
3668
- import_nestjs_agent_core22.AGENT_TOOL_REGISTRY,
3669
- import_nestjs_agent_core22.AGENT_REGISTRY,
3670
- import_nestjs_agent_core22.AGENT_SINK,
3671
- import_nestjs_agent_core22.AGENT_ROLES_POLICY,
3672
- import_nestjs_agent_core22.AGENT_ACTOR_RESOLVER,
3673
- import_nestjs_agent_core22.AGENT_MODEL,
3819
+ import_nestjs_agent_core23.AGENT_OPTIONS,
3820
+ import_nestjs_agent_core23.AGENT_TOOL_REGISTRY,
3821
+ import_nestjs_agent_core23.AGENT_REGISTRY,
3822
+ import_nestjs_agent_core23.AGENT_SINK,
3823
+ import_nestjs_agent_core23.AGENT_ROLES_POLICY,
3824
+ import_nestjs_agent_core23.AGENT_ACTOR_RESOLVER,
3825
+ import_nestjs_agent_core23.AGENT_MODEL,
3674
3826
  ...includeStore ? [
3675
- import_nestjs_agent_core22.AGENT_STORE
3827
+ import_nestjs_agent_core23.AGENT_STORE
3676
3828
  ] : [],
3677
- import_nestjs_agent_core22.AGENT_QUOTA_STORE,
3678
- import_nestjs_agent_core22.AGENT_MODEL_CATALOG,
3679
- import_nestjs_agent_core22.AGENT_PROMPT_CONTRIBUTORS,
3680
- import_nestjs_agent_core22.AGENT_SKILLS,
3681
- import_nestjs_agent_core22.AGENT_MEMORY,
3682
- import_nestjs_agent_core22.AGENT_DEPS_FACTORY,
3829
+ import_nestjs_agent_core23.AGENT_QUOTA_STORE,
3830
+ import_nestjs_agent_core23.AGENT_MODEL_CATALOG,
3831
+ import_nestjs_agent_core23.AGENT_QUOTA_PROVIDER,
3832
+ import_nestjs_agent_core23.AGENT_PROMPT_CONTRIBUTORS,
3833
+ import_nestjs_agent_core23.AGENT_SKILLS,
3834
+ import_nestjs_agent_core23.AGENT_MEMORY,
3835
+ import_nestjs_agent_core23.AGENT_DEPS_FACTORY,
3683
3836
  AgentDepsFactory2,
3684
3837
  AgentService,
3685
3838
  InlineAgentRunner,
3686
- import_nestjs_agent_core22.AGENT_APPROVAL_PORT
3839
+ import_nestjs_agent_core23.AGENT_APPROVAL_PORT
3687
3840
  ];
3688
3841
  }
3689
3842
  __name(exportsFor, "exportsFor");
@@ -3755,7 +3908,7 @@ var AgentModule = class _AgentModule {
3755
3908
  controllers: controllersForSurface(options.surface, options.attachments?.upload === true),
3756
3909
  providers: [
3757
3910
  {
3758
- provide: import_nestjs_agent_core22.AGENT_OPTIONS,
3911
+ provide: import_nestjs_agent_core23.AGENT_OPTIONS,
3759
3912
  useValue: options
3760
3913
  },
3761
3914
  ...sharedProviders(options.durable ?? false, includeStore),
@@ -3779,7 +3932,7 @@ var AgentModule = class _AgentModule {
3779
3932
  controllers: controllersForSurface(options.surface, options.attachmentsUpload === true),
3780
3933
  providers: [
3781
3934
  {
3782
- provide: import_nestjs_agent_core22.AGENT_OPTIONS,
3935
+ provide: import_nestjs_agent_core23.AGENT_OPTIONS,
3783
3936
  // Stamp the STATIC wiring flags onto the factory result: `durable`/`surface` are
3784
3937
  // authoritative on the async config object (they decided the actual wiring above), and
3785
3938
  // the durable module reads `durable` back off AGENT_OPTIONS. `surface` isn't read back by
@@ -3858,6 +4011,7 @@ __reExport(index_exports, require("@dudousxd/nestjs-agent-core"), module.exports
3858
4011
  HeaderActorResolver,
3859
4012
  InProcessTokenStreamSink,
3860
4013
  InlineAgentRunner,
4014
+ LedgerQuotaProvider,
3861
4015
  LedgerQuotaStore,
3862
4016
  MemoriesController,
3863
4017
  MessagesController,