@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.js CHANGED
@@ -52,6 +52,22 @@ async function seedModelPrices(store, prices) {
52
52
  }
53
53
  __name(seedModelPrices, "seedModelPrices");
54
54
 
55
+ // src/spi/governance-queries.ts
56
+ var THREAD_DETAIL_CONTENT_CHARS = 2e3;
57
+ function truncateDetailContent(content) {
58
+ if (content.length <= THREAD_DETAIL_CONTENT_CHARS) {
59
+ return {
60
+ content,
61
+ truncated: false
62
+ };
63
+ }
64
+ return {
65
+ content: content.slice(0, THREAD_DETAIL_CONTENT_CHARS),
66
+ truncated: true
67
+ };
68
+ }
69
+ __name(truncateDetailContent, "truncateDetailContent");
70
+
55
71
  // src/governance/compute.ts
56
72
  function estimateCost(usage, price) {
57
73
  if (price === void 0) {
@@ -157,6 +173,24 @@ function bucketByThread(rows, prices, threads, options) {
157
173
  return result.slice(0, options.limit);
158
174
  }
159
175
  __name(bucketByThread, "bucketByThread");
176
+ function rollupThreadUsage(rows, prices) {
177
+ const rollup = {
178
+ requests: 0,
179
+ inputTokens: 0,
180
+ outputTokens: 0,
181
+ totalTokens: 0,
182
+ costUsd: 0
183
+ };
184
+ for (const row of rows) {
185
+ rollup.requests += 1;
186
+ rollup.inputTokens += row.inputTokens;
187
+ rollup.outputTokens += row.outputTokens;
188
+ rollup.totalTokens += row.inputTokens + row.outputTokens;
189
+ rollup.costUsd += rowCost(row, prices);
190
+ }
191
+ return rollup;
192
+ }
193
+ __name(rollupThreadUsage, "rollupThreadUsage");
160
194
  function bucketUsageTrend(rows, prices) {
161
195
  const byDay = /* @__PURE__ */ new Map();
162
196
  for (const row of rows) {
@@ -189,6 +223,34 @@ function dayBoundsUtc(range) {
189
223
  __name(dayBoundsUtc, "dayBoundsUtc");
190
224
 
191
225
  // src/tool-filters.ts
226
+ async function isToolEnabled(spec, handler) {
227
+ const declared = typeof spec.enabled === "function" ? await spec.enabled() : spec.enabled ?? true;
228
+ if (!declared) {
229
+ return false;
230
+ }
231
+ return handler?.isEnabled === void 0 ? true : await handler.isEnabled();
232
+ }
233
+ __name(isToolEnabled, "isToolEnabled");
234
+ async function filterToolsByEnabled(entries) {
235
+ const checked = await Promise.all(entries.map(async (entry) => ({
236
+ entry,
237
+ enabled: await isToolEnabled(entry.spec, entry.handler)
238
+ })));
239
+ return checked.filter((row) => row.enabled).map((row) => row.entry);
240
+ }
241
+ __name(filterToolsByEnabled, "filterToolsByEnabled");
242
+ async function canActorUseTool(actor, handler) {
243
+ return handler?.canUse === void 0 ? true : await handler.canUse(actor);
244
+ }
245
+ __name(canActorUseTool, "canActorUseTool");
246
+ async function filterToolsByCanUse(entries, actor) {
247
+ const checked = await Promise.all(entries.map(async (entry) => ({
248
+ entry,
249
+ allowed: await canActorUseTool(actor, entry.handler)
250
+ })));
251
+ return checked.filter((row) => row.allowed).map((row) => row.entry);
252
+ }
253
+ __name(filterToolsByCanUse, "filterToolsByCanUse");
192
254
  async function filterToolsByRole(tools, actor, policy) {
193
255
  const checked = await Promise.all(tools.map(async (tool) => ({
194
256
  tool,
@@ -239,6 +301,16 @@ var ToolForbiddenError = class extends Error {
239
301
  this.name = "ToolForbiddenError";
240
302
  }
241
303
  };
304
+ var ToolDisabledError = class extends Error {
305
+ static {
306
+ __name(this, "ToolDisabledError");
307
+ }
308
+ toolName;
309
+ constructor(toolName) {
310
+ super(`Tool "${toolName}" is disabled in this deployment`), this.toolName = toolName;
311
+ this.name = "ToolDisabledError";
312
+ }
313
+ };
242
314
  var ToolNotFoundError = class extends Error {
243
315
  static {
244
316
  __name(this, "ToolNotFoundError");
@@ -282,10 +354,21 @@ var ToolRegistry = class {
282
354
  ...this.entries.values()
283
355
  ].map((entry) => entry.spec);
284
356
  }
285
- /** The tools to offer the model for this actor+agent, after the two filter layers. */
357
+ /**
358
+ * The tools to offer the model for this actor+agent, after the four filter layers: what this
359
+ * deployment has enabled, what this actor's role allows, what each tool's own `canUse` allows
360
+ * this actor, and finally what this agent pinned.
361
+ *
362
+ * Every layer only ever removes tools, so no arrangement of them can widen what a turn reaches.
363
+ */
286
364
  async definitionsFor(actor, policy, allowedTools) {
287
- const roleScoped = await filterToolsByRole(this.allSpecs(), actor, policy);
288
- const allowScoped = filterToolsByAllowList(roleScoped, allowedTools);
365
+ const live = await filterToolsByEnabled([
366
+ ...this.entries.values()
367
+ ]);
368
+ const allowedByRole = new Set((await filterToolsByRole(live.map((entry) => entry.spec), actor, policy)).map((spec) => spec.name));
369
+ const roleScoped = live.filter((entry) => allowedByRole.has(entry.spec.name));
370
+ const actorScoped = await filterToolsByCanUse(roleScoped, actor);
371
+ const allowScoped = filterToolsByAllowList(actorScoped.map((entry) => entry.spec), allowedTools);
289
372
  return allowScoped.map((spec) => ({
290
373
  name: spec.name,
291
374
  kind: spec.kind,
@@ -293,15 +376,25 @@ var ToolRegistry = class {
293
376
  inputSchema: spec.inputSchema
294
377
  }));
295
378
  }
296
- /** Run a tool. Re-checks the role (defense-in-depth) and re-parses the input via Zod. */
379
+ /**
380
+ * Run a tool. Re-checks that the tool is enabled and that the role allows it (defense-in-depth —
381
+ * a call can reach here from a replayed durable step or an approval granted before the flag
382
+ * moved, neither of which went through `definitionsFor` again) and re-parses the input via Zod.
383
+ */
297
384
  async invoke(name, input, ctx, policy) {
298
385
  const entry = this.entries.get(name);
299
386
  if (entry === void 0) {
300
387
  throw new ToolNotFoundError(name);
301
388
  }
389
+ if (!await isToolEnabled(entry.spec, entry.handler)) {
390
+ throw new ToolDisabledError(name);
391
+ }
302
392
  if (!await policy.can(ctx.actor, entry.spec)) {
303
393
  throw new ToolForbiddenError(name);
304
394
  }
395
+ if (!await canActorUseTool(ctx.actor, entry.handler)) {
396
+ throw new ToolForbiddenError(name);
397
+ }
305
398
  const validation = await entry.spec.inputSchema["~standard"].validate(input);
306
399
  if (validation.issues !== void 0) {
307
400
  throw new ToolInputInvalidError(name, validation.issues);
@@ -1178,6 +1271,8 @@ export {
1178
1271
  DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
1179
1272
  DefaultRolesPolicy,
1180
1273
  QuotaExceededError,
1274
+ THREAD_DETAIL_CONTENT_CHARS,
1275
+ ToolDisabledError,
1181
1276
  ToolForbiddenError,
1182
1277
  ToolInputInvalidError,
1183
1278
  ToolNotFoundError,
@@ -1187,12 +1282,16 @@ export {
1187
1282
  bucketByModel,
1188
1283
  bucketByThread,
1189
1284
  bucketUsageTrend,
1285
+ canActorUseTool,
1190
1286
  dayBoundsUtc,
1191
1287
  encodeStreamEvent,
1192
1288
  estimateCost,
1193
1289
  filterToolsByAllowList,
1290
+ filterToolsByCanUse,
1291
+ filterToolsByEnabled,
1194
1292
  filterToolsByRole,
1195
1293
  invokeWithTransientRetry,
1294
+ isToolEnabled,
1196
1295
  isTransientToolError,
1197
1296
  publishAgentDelegated,
1198
1297
  publishAgentMessage,
@@ -1204,10 +1303,12 @@ export {
1204
1303
  publishAgentToolCall,
1205
1304
  publishAgentToolRetry,
1206
1305
  resolveToolTransientRetryNumbers,
1306
+ rollupThreadUsage,
1207
1307
  runAgentLoop,
1208
1308
  seedModelPrices,
1209
1309
  traceLlmTurn,
1210
1310
  traceToolExecution,
1311
+ truncateDetailContent,
1211
1312
  withToolTimeout
1212
1313
  };
1213
1314
  //# sourceMappingURL=index.js.map