@dudousxd/nestjs-agent-core 0.10.0 → 0.12.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
@@ -49,6 +49,8 @@ __export(index_exports, {
49
49
  DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS: () => DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
50
50
  DefaultRolesPolicy: () => DefaultRolesPolicy,
51
51
  QuotaExceededError: () => QuotaExceededError,
52
+ THREAD_DETAIL_CONTENT_CHARS: () => THREAD_DETAIL_CONTENT_CHARS,
53
+ ToolDisabledError: () => ToolDisabledError,
52
54
  ToolForbiddenError: () => ToolForbiddenError,
53
55
  ToolInputInvalidError: () => ToolInputInvalidError,
54
56
  ToolNotFoundError: () => ToolNotFoundError,
@@ -58,12 +60,16 @@ __export(index_exports, {
58
60
  bucketByModel: () => bucketByModel,
59
61
  bucketByThread: () => bucketByThread,
60
62
  bucketUsageTrend: () => bucketUsageTrend,
63
+ canActorUseTool: () => canActorUseTool,
61
64
  dayBoundsUtc: () => dayBoundsUtc,
62
65
  encodeStreamEvent: () => encodeStreamEvent,
63
66
  estimateCost: () => estimateCost,
64
67
  filterToolsByAllowList: () => filterToolsByAllowList,
68
+ filterToolsByCanUse: () => filterToolsByCanUse,
69
+ filterToolsByEnabled: () => filterToolsByEnabled,
65
70
  filterToolsByRole: () => filterToolsByRole,
66
71
  invokeWithTransientRetry: () => invokeWithTransientRetry,
72
+ isToolEnabled: () => isToolEnabled,
67
73
  isTransientToolError: () => isTransientToolError,
68
74
  publishAgentDelegated: () => publishAgentDelegated,
69
75
  publishAgentMessage: () => publishAgentMessage,
@@ -75,10 +81,12 @@ __export(index_exports, {
75
81
  publishAgentToolCall: () => publishAgentToolCall,
76
82
  publishAgentToolRetry: () => publishAgentToolRetry,
77
83
  resolveToolTransientRetryNumbers: () => resolveToolTransientRetryNumbers,
84
+ rollupThreadUsage: () => rollupThreadUsage,
78
85
  runAgentLoop: () => runAgentLoop,
79
86
  seedModelPrices: () => seedModelPrices,
80
87
  traceLlmTurn: () => traceLlmTurn,
81
88
  traceToolExecution: () => traceToolExecution,
89
+ truncateDetailContent: () => truncateDetailContent,
82
90
  withToolTimeout: () => withToolTimeout
83
91
  });
84
92
  module.exports = __toCommonJS(index_exports);
@@ -134,6 +142,22 @@ async function seedModelPrices(store, prices) {
134
142
  }
135
143
  __name(seedModelPrices, "seedModelPrices");
136
144
 
145
+ // src/spi/governance-queries.ts
146
+ var THREAD_DETAIL_CONTENT_CHARS = 2e3;
147
+ function truncateDetailContent(content) {
148
+ if (content.length <= THREAD_DETAIL_CONTENT_CHARS) {
149
+ return {
150
+ content,
151
+ truncated: false
152
+ };
153
+ }
154
+ return {
155
+ content: content.slice(0, THREAD_DETAIL_CONTENT_CHARS),
156
+ truncated: true
157
+ };
158
+ }
159
+ __name(truncateDetailContent, "truncateDetailContent");
160
+
137
161
  // src/governance/compute.ts
138
162
  function estimateCost(usage, price) {
139
163
  if (price === void 0) {
@@ -239,6 +263,24 @@ function bucketByThread(rows, prices, threads, options) {
239
263
  return result.slice(0, options.limit);
240
264
  }
241
265
  __name(bucketByThread, "bucketByThread");
266
+ function rollupThreadUsage(rows, prices) {
267
+ const rollup = {
268
+ requests: 0,
269
+ inputTokens: 0,
270
+ outputTokens: 0,
271
+ totalTokens: 0,
272
+ costUsd: 0
273
+ };
274
+ for (const row of rows) {
275
+ rollup.requests += 1;
276
+ rollup.inputTokens += row.inputTokens;
277
+ rollup.outputTokens += row.outputTokens;
278
+ rollup.totalTokens += row.inputTokens + row.outputTokens;
279
+ rollup.costUsd += rowCost(row, prices);
280
+ }
281
+ return rollup;
282
+ }
283
+ __name(rollupThreadUsage, "rollupThreadUsage");
242
284
  function bucketUsageTrend(rows, prices) {
243
285
  const byDay = /* @__PURE__ */ new Map();
244
286
  for (const row of rows) {
@@ -271,6 +313,34 @@ function dayBoundsUtc(range) {
271
313
  __name(dayBoundsUtc, "dayBoundsUtc");
272
314
 
273
315
  // src/tool-filters.ts
316
+ async function isToolEnabled(spec, handler) {
317
+ const declared = typeof spec.enabled === "function" ? await spec.enabled() : spec.enabled ?? true;
318
+ if (!declared) {
319
+ return false;
320
+ }
321
+ return handler?.isEnabled === void 0 ? true : await handler.isEnabled();
322
+ }
323
+ __name(isToolEnabled, "isToolEnabled");
324
+ async function filterToolsByEnabled(entries) {
325
+ const checked = await Promise.all(entries.map(async (entry) => ({
326
+ entry,
327
+ enabled: await isToolEnabled(entry.spec, entry.handler)
328
+ })));
329
+ return checked.filter((row) => row.enabled).map((row) => row.entry);
330
+ }
331
+ __name(filterToolsByEnabled, "filterToolsByEnabled");
332
+ async function canActorUseTool(actor, handler) {
333
+ return handler?.canUse === void 0 ? true : await handler.canUse(actor);
334
+ }
335
+ __name(canActorUseTool, "canActorUseTool");
336
+ async function filterToolsByCanUse(entries, actor) {
337
+ const checked = await Promise.all(entries.map(async (entry) => ({
338
+ entry,
339
+ allowed: await canActorUseTool(actor, entry.handler)
340
+ })));
341
+ return checked.filter((row) => row.allowed).map((row) => row.entry);
342
+ }
343
+ __name(filterToolsByCanUse, "filterToolsByCanUse");
274
344
  async function filterToolsByRole(tools, actor, policy) {
275
345
  const checked = await Promise.all(tools.map(async (tool) => ({
276
346
  tool,
@@ -321,6 +391,16 @@ var ToolForbiddenError = class extends Error {
321
391
  this.name = "ToolForbiddenError";
322
392
  }
323
393
  };
394
+ var ToolDisabledError = class extends Error {
395
+ static {
396
+ __name(this, "ToolDisabledError");
397
+ }
398
+ toolName;
399
+ constructor(toolName) {
400
+ super(`Tool "${toolName}" is disabled in this deployment`), this.toolName = toolName;
401
+ this.name = "ToolDisabledError";
402
+ }
403
+ };
324
404
  var ToolNotFoundError = class extends Error {
325
405
  static {
326
406
  __name(this, "ToolNotFoundError");
@@ -364,10 +444,21 @@ var ToolRegistry = class {
364
444
  ...this.entries.values()
365
445
  ].map((entry) => entry.spec);
366
446
  }
367
- /** The tools to offer the model for this actor+agent, after the two filter layers. */
447
+ /**
448
+ * The tools to offer the model for this actor+agent, after the four filter layers: what this
449
+ * deployment has enabled, what this actor's role allows, what each tool's own `canUse` allows
450
+ * this actor, and finally what this agent pinned.
451
+ *
452
+ * Every layer only ever removes tools, so no arrangement of them can widen what a turn reaches.
453
+ */
368
454
  async definitionsFor(actor, policy, allowedTools) {
369
- const roleScoped = await filterToolsByRole(this.allSpecs(), actor, policy);
370
- const allowScoped = filterToolsByAllowList(roleScoped, allowedTools);
455
+ const live = await filterToolsByEnabled([
456
+ ...this.entries.values()
457
+ ]);
458
+ const allowedByRole = new Set((await filterToolsByRole(live.map((entry) => entry.spec), actor, policy)).map((spec) => spec.name));
459
+ const roleScoped = live.filter((entry) => allowedByRole.has(entry.spec.name));
460
+ const actorScoped = await filterToolsByCanUse(roleScoped, actor);
461
+ const allowScoped = filterToolsByAllowList(actorScoped.map((entry) => entry.spec), allowedTools);
371
462
  return allowScoped.map((spec) => ({
372
463
  name: spec.name,
373
464
  kind: spec.kind,
@@ -375,15 +466,25 @@ var ToolRegistry = class {
375
466
  inputSchema: spec.inputSchema
376
467
  }));
377
468
  }
378
- /** Run a tool. Re-checks the role (defense-in-depth) and re-parses the input via Zod. */
469
+ /**
470
+ * Run a tool. Re-checks that the tool is enabled and that the role allows it (defense-in-depth —
471
+ * a call can reach here from a replayed durable step or an approval granted before the flag
472
+ * moved, neither of which went through `definitionsFor` again) and re-parses the input via Zod.
473
+ */
379
474
  async invoke(name, input, ctx, policy) {
380
475
  const entry = this.entries.get(name);
381
476
  if (entry === void 0) {
382
477
  throw new ToolNotFoundError(name);
383
478
  }
479
+ if (!await isToolEnabled(entry.spec, entry.handler)) {
480
+ throw new ToolDisabledError(name);
481
+ }
384
482
  if (!await policy.can(ctx.actor, entry.spec)) {
385
483
  throw new ToolForbiddenError(name);
386
484
  }
485
+ if (!await canActorUseTool(ctx.actor, entry.handler)) {
486
+ throw new ToolForbiddenError(name);
487
+ }
387
488
  const validation = await entry.spec.inputSchema["~standard"].validate(input);
388
489
  if (validation.issues !== void 0) {
389
490
  throw new ToolInputInvalidError(name, validation.issues);
@@ -1261,6 +1362,8 @@ __name(runAgentLoop, "runAgentLoop");
1261
1362
  DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
1262
1363
  DefaultRolesPolicy,
1263
1364
  QuotaExceededError,
1365
+ THREAD_DETAIL_CONTENT_CHARS,
1366
+ ToolDisabledError,
1264
1367
  ToolForbiddenError,
1265
1368
  ToolInputInvalidError,
1266
1369
  ToolNotFoundError,
@@ -1270,12 +1373,16 @@ __name(runAgentLoop, "runAgentLoop");
1270
1373
  bucketByModel,
1271
1374
  bucketByThread,
1272
1375
  bucketUsageTrend,
1376
+ canActorUseTool,
1273
1377
  dayBoundsUtc,
1274
1378
  encodeStreamEvent,
1275
1379
  estimateCost,
1276
1380
  filterToolsByAllowList,
1381
+ filterToolsByCanUse,
1382
+ filterToolsByEnabled,
1277
1383
  filterToolsByRole,
1278
1384
  invokeWithTransientRetry,
1385
+ isToolEnabled,
1279
1386
  isTransientToolError,
1280
1387
  publishAgentDelegated,
1281
1388
  publishAgentMessage,
@@ -1287,10 +1394,12 @@ __name(runAgentLoop, "runAgentLoop");
1287
1394
  publishAgentToolCall,
1288
1395
  publishAgentToolRetry,
1289
1396
  resolveToolTransientRetryNumbers,
1397
+ rollupThreadUsage,
1290
1398
  runAgentLoop,
1291
1399
  seedModelPrices,
1292
1400
  traceLlmTurn,
1293
1401
  traceToolExecution,
1402
+ truncateDetailContent,
1294
1403
  withToolTimeout
1295
1404
  });
1296
1405
  //# sourceMappingURL=index.cjs.map