@dudousxd/nestjs-agent-dashboard 0.20.0 → 0.21.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.
@@ -40,6 +40,7 @@ __export(index_exports, {
40
40
  AgentApiController: () => AgentApiController,
41
41
  AgentApiModule: () => AgentApiModule,
42
42
  AgentDashboardModule: () => AgentDashboardModule,
43
+ AgentDevToolsRegistration: () => AgentDevToolsRegistration,
43
44
  AgentUiController: () => AgentUiController,
44
45
  CONSOLE_AUTHORIZE_MISSING_WARNING: () => CONSOLE_AUTHORIZE_MISSING_WARNING,
45
46
  CONSOLE_GATE: () => CONSOLE_GATE,
@@ -52,10 +53,13 @@ __export(index_exports, {
52
53
  DASHBOARD_APPROVAL_ACTOR_REF: () => DASHBOARD_APPROVAL_ACTOR_REF,
53
54
  DASHBOARD_BASE_PATH: () => DASHBOARD_BASE_PATH,
54
55
  DASHBOARD_CONSOLE_OPTIONS: () => DASHBOARD_CONSOLE_OPTIONS,
56
+ DASHBOARD_DEV_TOOLS: () => DASHBOARD_DEV_TOOLS,
55
57
  DashboardConsoleAccess: () => DashboardConsoleAccess,
56
58
  DashboardService: () => DashboardService,
57
59
  UseConsoleAuthorize: () => UseConsoleAuthorize,
58
60
  agentDashboardMountPaths: () => agentDashboardMountPaths,
61
+ agentDevTools: () => agentDevTools,
62
+ anyAuthorize: () => anyAuthorize,
59
63
  authorizeByToken: () => authorizeByToken,
60
64
  normalizeConsoleResult: () => normalizeConsoleResult,
61
65
  parsePriceInput: () => parsePriceInput,
@@ -145,6 +149,7 @@ var DASHBOARD_BASE_PATH = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-das
145
149
  var DASHBOARD_API_PATH = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:api-path");
146
150
  var DASHBOARD_APPROVAL_ACTOR_REF = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:approval-actor-ref");
147
151
  var DASHBOARD_CONSOLE_OPTIONS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:console-options");
152
+ var DASHBOARD_DEV_TOOLS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:dev-tools");
148
153
 
149
154
  // src/server/dashboard.service.ts
150
155
  function _ts_decorate(decorators, target, key, desc) {
@@ -223,8 +228,8 @@ var DashboardService = class {
223
228
  };
224
229
  }
225
230
  /** Top threads by cost for a day range (default 10, highest cost first). */
226
- async topThreads(range, limit = 10) {
227
- const rows = await this.queries.spendByThread(range, limit);
231
+ async topThreads(range, limit2 = 10) {
232
+ const rows = await this.queries.spendByThread(range, limit2);
228
233
  return this.withActorLabels(rows);
229
234
  }
230
235
  /** Run reliability for a day range: metrics, by-agent/by-error breakdowns and the trend, in parallel. */
@@ -243,12 +248,12 @@ var DashboardService = class {
243
248
  };
244
249
  }
245
250
  /** Most recent runs (status/agent/duration/error) for the Reliability recent-runs table. */
246
- recentRuns(limit) {
247
- return this.queries.recentRuns(limit);
251
+ recentRuns(limit2) {
252
+ return this.queries.recentRuns(limit2);
248
253
  }
249
254
  /** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */
250
- recentToolCalls(limit) {
251
- return this.queries.recentToolCalls(limit);
255
+ recentToolCalls(limit2) {
256
+ return this.queries.recentToolCalls(limit2);
252
257
  }
253
258
  /** Paged, filterable tool calls for the Runs & tools activity table. */
254
259
  toolCallsPage(query) {
@@ -263,8 +268,8 @@ var DashboardService = class {
263
268
  * Capped and totalless — kept because the SPA still calls `GET approvals`; new callers want
264
269
  * {@link approvalsPage}, which reports how much of the backlog the cap hid.
265
270
  */
266
- pendingApprovals(limit) {
267
- return this.queries.pendingApprovals(limit);
271
+ pendingApprovals(limit2) {
272
+ return this.queries.pendingApprovals(limit2);
268
273
  }
269
274
  /** Paged, filterable approvals inbox with a total — the backlog-safe read of the HITL queue. */
270
275
  approvalsPage(query) {
@@ -352,8 +357,8 @@ var DashboardService = class {
352
357
  });
353
358
  }
354
359
  /** Most recent threads with rolled-up message/token counts. */
355
- async recentThreads(limit) {
356
- const rows = await this.queries.recentThreads(limit);
360
+ async recentThreads(limit2) {
361
+ const rows = await this.queries.recentThreads(limit2);
357
362
  return this.withActorLabels(rows);
358
363
  }
359
364
  /**
@@ -361,10 +366,10 @@ var DashboardService = class {
361
366
  * {@link recentThreads} is.
362
367
  */
363
368
  async threadsPage(query) {
364
- const page = await this.queries.threadsPage(query);
365
- const rows = await this.withActorLabels(page.rows);
369
+ const page2 = await this.queries.threadsPage(query);
370
+ const rows = await this.withActorLabels(page2.rows);
366
371
  return {
367
- ...page,
372
+ ...page2,
368
373
  rows
369
374
  };
370
375
  }
@@ -685,27 +690,27 @@ var AgentApiController = class {
685
690
  return this.dashboard.spend(resolveRange(from, to));
686
691
  }
687
692
  /** Top threads by cost (default 10, max 200) for a day range (defaults to the last 30 days). */
688
- topThreads(from, to, limit) {
689
- return this.dashboard.topThreads(resolveRange(from, to), parseLimit(limit, 10));
693
+ topThreads(from, to, limit2) {
694
+ return this.dashboard.topThreads(resolveRange(from, to), parseLimit(limit2, 10));
690
695
  }
691
696
  /** `{ metrics, byAgent, errors, trend }` for a day range (defaults to the last 30 days). */
692
697
  reliability(from, to) {
693
698
  return this.dashboard.reliability(resolveRange(from, to));
694
699
  }
695
700
  /** Most recent runs (default 50, max 200) for the Reliability recent-runs table. */
696
- runs(limit) {
697
- return this.dashboard.recentRuns(parseLimit(limit, 50));
701
+ runs(limit2) {
702
+ return this.dashboard.recentRuns(parseLimit(limit2, 50));
698
703
  }
699
704
  /**
700
705
  * Paged, filterable runs for the Reliability recent-runs table. `page` (default 1), `limit`
701
706
  * (default 25, max 200), and `where[agentName]`/`where[status]`/`where[errorCode]`/
702
707
  * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
703
708
  */
704
- runsPage(page, limit, where) {
705
- const parsedWhere = parseRunWhere(where);
709
+ runsPage(page2, limit2, where2) {
710
+ const parsedWhere = parseRunWhere(where2);
706
711
  return this.dashboard.runsPage({
707
- page: parsePageNumber(page),
708
- pageSize: parseLimit(limit, 25),
712
+ page: parsePageNumber(page2),
713
+ pageSize: parseLimit(limit2, 25),
709
714
  ...parsedWhere !== void 0 ? {
710
715
  where: parsedWhere
711
716
  } : {}
@@ -722,19 +727,19 @@ var AgentApiController = class {
722
727
  return this.dashboard.runDetail(runId);
723
728
  }
724
729
  /** Most recent tool calls (default 50, max 200) for the activity feed. */
725
- toolCalls(limit) {
726
- return this.dashboard.recentToolCalls(parseLimit(limit, 50));
730
+ toolCalls(limit2) {
731
+ return this.dashboard.recentToolCalls(parseLimit(limit2, 50));
727
732
  }
728
733
  /**
729
734
  * Paged, filterable tool calls for the Runs & tools activity table. `page` (default 1), `limit`
730
735
  * (default 25, max 200), and `where[toolName]`/`where[toolType]`/`where[status]`/`where[threadId]`/
731
736
  * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
732
737
  */
733
- toolCallsPage(page, limit, where) {
734
- const parsedWhere = parseToolCallWhere(where);
738
+ toolCallsPage(page2, limit2, where2) {
739
+ const parsedWhere = parseToolCallWhere(where2);
735
740
  return this.dashboard.toolCallsPage({
736
- page: parsePageNumber(page),
737
- pageSize: parseLimit(limit, 25),
741
+ page: parsePageNumber(page2),
742
+ pageSize: parseLimit(limit2, 25),
738
743
  ...parsedWhere !== void 0 ? {
739
744
  where: parsedWhere
740
745
  } : {}
@@ -746,8 +751,8 @@ var AgentApiController = class {
746
751
  * Capped with NO total, so a backlog past `limit` is invisible from the response alone. Kept
747
752
  * because the SPA still calls it; anything new should call `approvals-page` instead.
748
753
  */
749
- approvals(limit) {
750
- return this.dashboard.pendingApprovals(parseLimit(limit, 50));
754
+ approvals(limit2) {
755
+ return this.dashboard.pendingApprovals(parseLimit(limit2, 50));
751
756
  }
752
757
  /**
753
758
  * Paged, filterable approvals inbox, oldest first. `page` (default 1), `limit` (default 25, max
@@ -757,11 +762,11 @@ var AgentApiController = class {
757
762
  * Unlike `GET approvals` this reports `total`, so the console can say "showing N of M" instead of
758
763
  * silently hiding pending decisions behind a row cap.
759
764
  */
760
- approvalsPage(page, limit, where) {
761
- const parsedWhere = parseApprovalWhere(where);
765
+ approvalsPage(page2, limit2, where2) {
766
+ const parsedWhere = parseApprovalWhere(where2);
762
767
  return this.dashboard.approvalsPage({
763
- page: parsePageNumber(page),
764
- pageSize: parseLimit(limit, 25),
768
+ page: parsePageNumber(page2),
769
+ pageSize: parseLimit(limit2, 25),
765
770
  ...parsedWhere !== void 0 ? {
766
771
  where: parsedWhere
767
772
  } : {}
@@ -808,19 +813,19 @@ var AgentApiController = class {
808
813
  return this.dashboard.genuiStats(resolveRange(from, to));
809
814
  }
810
815
  /** Most recent threads (default 50, max 200) with rolled-up counts. */
811
- threads(limit) {
812
- return this.dashboard.recentThreads(parseLimit(limit, 50));
816
+ threads(limit2) {
817
+ return this.dashboard.recentThreads(parseLimit(limit2, 50));
813
818
  }
814
819
  /**
815
820
  * Paged, filterable threads for the Runs & tools threads table. `page` (default 1), `limit`
816
821
  * (default 25, max 200), and `where[actorRef]`/`where[title]` (case-insensitive substring)/
817
822
  * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
818
823
  */
819
- threadsPage(page, limit, where) {
820
- const parsedWhere = parseThreadWhere(where);
824
+ threadsPage(page2, limit2, where2) {
825
+ const parsedWhere = parseThreadWhere(where2);
821
826
  return this.dashboard.threadsPage({
822
- page: parsePageNumber(page),
823
- pageSize: parseLimit(limit, 25),
827
+ page: parsePageNumber(page2),
828
+ pageSize: parseLimit(limit2, 25),
824
829
  ...parsedWhere !== void 0 ? {
825
830
  where: parsedWhere
826
831
  } : {}
@@ -1106,8 +1111,8 @@ __name(agentDashboardMountPaths, "agentDashboardMountPaths");
1106
1111
  // src/server/agent-dashboard.module.ts
1107
1112
  var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
1108
1113
  var import_reflect_metadata = require("reflect-metadata");
1109
- var import_common9 = require("@nestjs/common");
1110
- var import_core2 = require("@nestjs/core");
1114
+ var import_common10 = require("@nestjs/common");
1115
+ var import_core3 = require("@nestjs/core");
1111
1116
 
1112
1117
  // src/server/agent-ui.controller.ts
1113
1118
  var import_node_fs = require("fs");
@@ -1118,19 +1123,23 @@ var import_common8 = require("@nestjs/common");
1118
1123
 
1119
1124
  // src/server/console-authorize.guard.ts
1120
1125
  var import_common7 = require("@nestjs/common");
1121
- var import_core = require("@nestjs/core");
1126
+ var import_core2 = require("@nestjs/core");
1122
1127
 
1123
1128
  // src/server/console-authorize.ts
1124
1129
  var import_node_crypto = require("crypto");
1125
1130
  var import_common6 = require("@nestjs/common");
1131
+ var import_core = require("@nestjs/core");
1126
1132
  var CONSOLE_GATE = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:console-gate");
1127
1133
  var PACKAGE = "@dudousxd/nestjs-agent-dashboard";
1128
1134
  var MODULE = "AgentDashboardModule";
1129
1135
  var logger = new import_common6.Logger(MODULE);
1130
1136
  var CONSOLE_AUTHORIZE_MISSING_WARNING = `[${PACKAGE}] console has no \`authorize\` configured: it is open outside production and denies every request in production. Set \`authorize\` in ${MODULE}.forRoot().`;
1131
- var allow = /* @__PURE__ */ __name(() => ({
1137
+ var allow = /* @__PURE__ */ __name((caller) => caller !== void 0 && caller !== "" ? {
1138
+ allowed: true,
1139
+ caller
1140
+ } : {
1132
1141
  allowed: true
1133
- }), "allow");
1142
+ }, "allow");
1134
1143
  var forbidden = /* @__PURE__ */ __name(() => ({
1135
1144
  allowed: false,
1136
1145
  reason: "forbidden"
@@ -1154,7 +1163,9 @@ function normalizeConsoleResult(result) {
1154
1163
  redirectTo: value.redirectTo
1155
1164
  };
1156
1165
  }
1157
- if (value.allowed === true) return allow();
1166
+ if (value.allowed === true) {
1167
+ return allow(typeof value.caller === "string" ? value.caller : void 0);
1168
+ }
1158
1169
  return value.reason === "unauthenticated" ? unauthenticated() : forbidden();
1159
1170
  }
1160
1171
  __name(normalizeConsoleResult, "normalizeConsoleResult");
@@ -1230,7 +1241,7 @@ async function resolveConsoleAuthorize(authorize, moduleRef, options = {}) {
1230
1241
  if (authorize === void 0) {
1231
1242
  if (options.warnIfMissing !== false) log.warn(CONSOLE_AUTHORIZE_MISSING_WARNING);
1232
1243
  const production = isProduction();
1233
- return async () => production ? forbidden() : allow();
1244
+ return async () => production ? forbidden() : allow("dev-open");
1234
1245
  }
1235
1246
  const instanceOf = classInstances(moduleRef);
1236
1247
  let decide;
@@ -1280,12 +1291,18 @@ function authorizeByToken(options) {
1280
1291
  throw new Error("authorizeByToken: `token` is empty; set it (e.g. from an env var).");
1281
1292
  }
1282
1293
  const expected = sha256(token);
1294
+ const caller = `token:${expected.toString("hex").slice(0, 12)}`;
1283
1295
  return (context) => {
1284
1296
  const req = context.switchToHttp().getRequest();
1285
1297
  const header = req?.headers?.authorization;
1286
1298
  const value = Array.isArray(header) ? header[0] : header;
1287
1299
  const match = typeof value === "string" ? /^Bearer\s+(.+)$/i.exec(value.trim()) : null;
1288
- if (match?.[1] !== void 0 && (0, import_node_crypto.timingSafeEqual)(sha256(match[1]), expected)) return true;
1300
+ if (match?.[1] !== void 0 && (0, import_node_crypto.timingSafeEqual)(sha256(match[1]), expected)) {
1301
+ return {
1302
+ allowed: true,
1303
+ caller
1304
+ };
1305
+ }
1289
1306
  return {
1290
1307
  allowed: false,
1291
1308
  reason: "unauthenticated"
@@ -1293,6 +1310,48 @@ function authorizeByToken(options) {
1293
1310
  };
1294
1311
  }
1295
1312
  __name(authorizeByToken, "authorizeByToken");
1313
+ function anyAuthorize(...forms) {
1314
+ if (forms.length === 0) throw new Error("anyAuthorize: pass at least one authorize form.");
1315
+ let AnyConsoleAuthorizer = class AnyConsoleAuthorizer {
1316
+ static {
1317
+ __name(this, "AnyConsoleAuthorizer");
1318
+ }
1319
+ moduleRef;
1320
+ #decides;
1321
+ constructor(moduleRef) {
1322
+ this.moduleRef = moduleRef;
1323
+ }
1324
+ async authorize(context) {
1325
+ this.#decides ??= Promise.all(forms.map((form) => resolveConsoleAuthorize(form, this.moduleRef, {
1326
+ warnIfMissing: false
1327
+ })));
1328
+ this.#decides.catch(() => {
1329
+ this.#decides = void 0;
1330
+ });
1331
+ let unauthenticated2;
1332
+ for (const decide of await this.#decides) {
1333
+ const decision = await decide(context);
1334
+ if (decision.allowed) return decision;
1335
+ if (decision.reason === "unauthenticated") unauthenticated2 ??= decision;
1336
+ }
1337
+ if (unauthenticated2 === void 0) return {
1338
+ allowed: false,
1339
+ reason: "forbidden"
1340
+ };
1341
+ return "redirectTo" in unauthenticated2 && unauthenticated2.redirectTo !== void 0 ? {
1342
+ redirectTo: unauthenticated2.redirectTo
1343
+ } : {
1344
+ allowed: false,
1345
+ reason: "unauthenticated"
1346
+ };
1347
+ }
1348
+ };
1349
+ Reflect.defineMetadata("design:paramtypes", [
1350
+ import_core.ModuleRef
1351
+ ], AnyConsoleAuthorizer);
1352
+ return AnyConsoleAuthorizer;
1353
+ }
1354
+ __name(anyAuthorize, "anyAuthorize");
1296
1355
 
1297
1356
  // src/server/console-authorize.guard.ts
1298
1357
  function _ts_decorate3(decorators, target, key, desc) {
@@ -1350,7 +1409,7 @@ DashboardConsoleAccess = _ts_decorate3([
1350
1409
  _ts_metadata3("design:type", Function),
1351
1410
  _ts_metadata3("design:paramtypes", [
1352
1411
  typeof DashboardConsoleOptions === "undefined" ? Object : DashboardConsoleOptions,
1353
- typeof import_core.ModuleRef === "undefined" ? Object : import_core.ModuleRef
1412
+ typeof import_core2.ModuleRef === "undefined" ? Object : import_core2.ModuleRef
1354
1413
  ])
1355
1414
  ], DashboardConsoleAccess);
1356
1415
  var CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-agent-dashboard:console-page";
@@ -1361,7 +1420,7 @@ var ConsoleAccessDenied = class extends import_common7.HttpException {
1361
1420
  }
1362
1421
  decision;
1363
1422
  page;
1364
- constructor(decision, page) {
1423
+ constructor(decision, page2) {
1365
1424
  super(decision.reason === "forbidden" ? {
1366
1425
  error: "forbidden"
1367
1426
  } : {
@@ -1369,13 +1428,13 @@ var ConsoleAccessDenied = class extends import_common7.HttpException {
1369
1428
  ...decision.redirectTo !== void 0 ? {
1370
1429
  redirectTo: decision.redirectTo
1371
1430
  } : {}
1372
- }, decision.reason === "forbidden" ? 403 : 401), this.decision = decision, this.page = page;
1431
+ }, decision.reason === "forbidden" ? 403 : 401), this.decision = decision, this.page = page2;
1373
1432
  }
1374
1433
  };
1375
1434
  function isPageNavigation(context) {
1376
1435
  if (context.getType() !== "http") return false;
1377
- const page = Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getHandler()) === true || Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) === true;
1378
- if (!page) return false;
1436
+ const page2 = Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getHandler()) === true || Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) === true;
1437
+ if (!page2) return false;
1379
1438
  const req = context.switchToHttp().getRequest();
1380
1439
  if ((req.method ?? "GET").toUpperCase() !== "GET") return false;
1381
1440
  const accept = req.headers?.accept;
@@ -1422,12 +1481,12 @@ var ConsoleAuthorizeFilter = class {
1422
1481
  catch(exception, host) {
1423
1482
  const adapter = this.adapterHost.httpAdapter;
1424
1483
  const res = host.switchToHttp().getResponse();
1425
- const { decision, page } = exception;
1426
- if (page && decision.reason === "unauthenticated" && decision.redirectTo !== void 0) {
1484
+ const { decision, page: page2 } = exception;
1485
+ if (page2 && decision.reason === "unauthenticated" && decision.redirectTo !== void 0) {
1427
1486
  adapter.redirect(res, 302, decision.redirectTo);
1428
1487
  return;
1429
1488
  }
1430
- if (page) {
1489
+ if (page2) {
1431
1490
  const forbidden2 = decision.reason === "forbidden";
1432
1491
  adapter.setHeader(res, "Content-Type", "text/html; charset=utf-8");
1433
1492
  adapter.reply(res, forbidden2 ? FORBIDDEN_PAGE : UNAUTHENTICATED_PAGE, forbidden2 ? 403 : 401);
@@ -1441,7 +1500,7 @@ ConsoleAuthorizeFilter = _ts_decorate3([
1441
1500
  (0, import_common7.Injectable)(),
1442
1501
  _ts_metadata3("design:type", Function),
1443
1502
  _ts_metadata3("design:paramtypes", [
1444
- typeof import_core.HttpAdapterHost === "undefined" ? Object : import_core.HttpAdapterHost
1503
+ typeof import_core2.HttpAdapterHost === "undefined" ? Object : import_core2.HttpAdapterHost
1445
1504
  ])
1446
1505
  ], ConsoleAuthorizeFilter);
1447
1506
  function UseConsoleAuthorize() {
@@ -1569,7 +1628,8 @@ AgentUiController = _ts_decorate4([
1569
1628
  ])
1570
1629
  ], AgentUiController);
1571
1630
 
1572
- // src/server/agent-dashboard.module.ts
1631
+ // src/server/dev-tools.ts
1632
+ var import_common9 = require("@nestjs/common");
1573
1633
  function _ts_decorate5(decorators, target, key, desc) {
1574
1634
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1575
1635
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -1584,6 +1644,411 @@ function _ts_decorate5(decorators, target, key, desc) {
1584
1644
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1585
1645
  }
1586
1646
  __name(_ts_decorate5, "_ts_decorate");
1647
+ function _ts_metadata5(metadataKey, metadataValue) {
1648
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
1649
+ return Reflect.metadata(metadataKey, metadataValue);
1650
+ }
1651
+ }
1652
+ __name(_ts_metadata5, "_ts_metadata");
1653
+ function _ts_param5(paramIndex, decorator) {
1654
+ return function(target, key) {
1655
+ decorator(target, key, paramIndex);
1656
+ };
1657
+ }
1658
+ __name(_ts_param5, "_ts_param");
1659
+ var DEV_TOOLS_SLOT = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:dev-tools");
1660
+ function devToolsSlot() {
1661
+ const holder = globalThis;
1662
+ const existing = holder[DEV_TOOLS_SLOT];
1663
+ if (existing !== void 0) return existing;
1664
+ const libs = [];
1665
+ const slot = {
1666
+ version: 1,
1667
+ libs,
1668
+ register(lib) {
1669
+ if (libs.some((entry) => entry.lib === lib.lib)) {
1670
+ throw new Error(`dev tools: the lib "${lib.lib}" is already registered`);
1671
+ }
1672
+ libs.push(lib);
1673
+ return () => {
1674
+ const index = libs.indexOf(lib);
1675
+ if (index !== -1) libs.splice(index, 1);
1676
+ };
1677
+ }
1678
+ };
1679
+ holder[DEV_TOOLS_SLOT] = slot;
1680
+ return slot;
1681
+ }
1682
+ __name(devToolsSlot, "devToolsSlot");
1683
+ var DEFAULT_LIMIT = 20;
1684
+ var MAX_LIMIT = 100;
1685
+ var MESSAGE_LIMIT = 50;
1686
+ var MESSAGE_BYTES = 2 * 1024;
1687
+ var DAY_MS2 = 864e5;
1688
+ var ISO_DAY3 = /^\d{4}-\d{2}-\d{2}$/;
1689
+ var day = {
1690
+ type: "string",
1691
+ description: "UTC day, YYYY-MM-DD."
1692
+ };
1693
+ var page = {
1694
+ type: "integer",
1695
+ description: "Page, from 1. Pass the `next` of the previous page.",
1696
+ minimum: 1
1697
+ };
1698
+ var limit = {
1699
+ type: "integer",
1700
+ description: `Rows per page (default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}).`,
1701
+ minimum: 1,
1702
+ maximum: MAX_LIMIT
1703
+ };
1704
+ var str = /* @__PURE__ */ __name((value) => typeof value === "string" && value !== "" ? value : void 0, "str");
1705
+ function dayOf(input, key) {
1706
+ const value = str(input[key]);
1707
+ if (value !== void 0 && !ISO_DAY3.test(value)) throw new Error(`"${key}" must be YYYY-MM-DD`);
1708
+ return value;
1709
+ }
1710
+ __name(dayOf, "dayOf");
1711
+ function rangeOf(input) {
1712
+ const utcDay2 = /* @__PURE__ */ __name((daysAgo) => new Date(Date.now() - daysAgo * DAY_MS2).toISOString().slice(0, 10), "utcDay");
1713
+ return {
1714
+ fromDay: dayOf(input, "from") ?? utcDay2(29),
1715
+ toDay: dayOf(input, "to") ?? utcDay2(0)
1716
+ };
1717
+ }
1718
+ __name(rangeOf, "rangeOf");
1719
+ function pageOf(input) {
1720
+ return {
1721
+ page: typeof input.page === "number" ? input.page : 1,
1722
+ pageSize: typeof input.limit === "number" ? input.limit : DEFAULT_LIMIT
1723
+ };
1724
+ }
1725
+ __name(pageOf, "pageOf");
1726
+ function where(fields) {
1727
+ const set = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== void 0));
1728
+ return Object.keys(set).length > 0 ? {
1729
+ where: set
1730
+ } : {};
1731
+ }
1732
+ __name(where, "where");
1733
+ function listOf(result) {
1734
+ return {
1735
+ items: result.rows,
1736
+ next: result.page * result.pageSize < result.total ? result.page + 1 : null,
1737
+ total: result.total
1738
+ };
1739
+ }
1740
+ __name(listOf, "listOf");
1741
+ function cut(text, maxBytes) {
1742
+ const size = Buffer.byteLength(text, "utf8");
1743
+ if (size <= maxBytes) return text;
1744
+ const suffix = `\u2026(truncated, ${size} bytes)`;
1745
+ const keep = Math.max(0, maxBytes - Buffer.byteLength(suffix, "utf8"));
1746
+ let head = Buffer.from(text, "utf8").subarray(0, keep).toString("utf8");
1747
+ if (head.endsWith("\uFFFD")) head = head.slice(0, -1);
1748
+ return `${head}${suffix}`;
1749
+ }
1750
+ __name(cut, "cut");
1751
+ function agentDevTools(dashboard) {
1752
+ return [
1753
+ {
1754
+ name: "agent_list_runs",
1755
+ kind: "read",
1756
+ description: "Agent runs, newest first: agent, actor, status, duration, cost, error. Filters: agent, status, errorCode, threadId, from/to (UTC days). Returns { items, next, total }; pass next as page.",
1757
+ input: {
1758
+ type: "object",
1759
+ properties: {
1760
+ agent: {
1761
+ type: "string",
1762
+ description: "Agent name."
1763
+ },
1764
+ status: {
1765
+ type: "string",
1766
+ enum: [
1767
+ "running",
1768
+ "completed",
1769
+ "failed",
1770
+ "cancelled"
1771
+ ]
1772
+ },
1773
+ errorCode: {
1774
+ type: "string"
1775
+ },
1776
+ threadId: {
1777
+ type: "string"
1778
+ },
1779
+ from: day,
1780
+ to: day,
1781
+ page,
1782
+ limit
1783
+ }
1784
+ },
1785
+ async run(input) {
1786
+ return listOf(await dashboard.runsPage({
1787
+ ...pageOf(input),
1788
+ ...where({
1789
+ agentName: str(input.agent),
1790
+ status: str(input.status),
1791
+ errorCode: str(input.errorCode),
1792
+ threadId: str(input.threadId),
1793
+ fromDay: dayOf(input, "from"),
1794
+ toDay: dayOf(input, "to")
1795
+ })
1796
+ }));
1797
+ }
1798
+ },
1799
+ {
1800
+ name: "agent_get_run",
1801
+ kind: "read",
1802
+ description: "One run: the run, its thread, its tool calls and timings. With includeMessages: true, the thread's latest 50 messages, each cut to 2 KB. An error when no run has that id.",
1803
+ input: {
1804
+ type: "object",
1805
+ properties: {
1806
+ id: {
1807
+ type: "string",
1808
+ description: "Run id."
1809
+ },
1810
+ includeMessages: {
1811
+ type: "boolean",
1812
+ description: "Include messages (default false)."
1813
+ }
1814
+ },
1815
+ required: [
1816
+ "id"
1817
+ ]
1818
+ },
1819
+ async run(input) {
1820
+ let detail;
1821
+ try {
1822
+ detail = await dashboard.runDetail(String(input.id));
1823
+ } catch (error) {
1824
+ if (error instanceof import_common9.NotFoundException) throw new Error(`run ${String(input.id)} not found`);
1825
+ throw error;
1826
+ }
1827
+ if (input.includeMessages !== true) return detail;
1828
+ const thread = await dashboard.threadDetail({
1829
+ threadId: detail.run.threadId,
1830
+ messageLimit: MESSAGE_LIMIT,
1831
+ runLimit: 1
1832
+ });
1833
+ return {
1834
+ ...detail,
1835
+ messages: thread.messages.map((message) => ({
1836
+ ...message,
1837
+ content: cut(message.content, MESSAGE_BYTES)
1838
+ }))
1839
+ };
1840
+ }
1841
+ },
1842
+ {
1843
+ name: "agent_get_spend",
1844
+ kind: "read",
1845
+ description: "Tokens and cost over UTC days from/to (default the last 30 days): by model, by actor, by feature and the daily trend. `by` returns one of them.",
1846
+ input: {
1847
+ type: "object",
1848
+ properties: {
1849
+ by: {
1850
+ type: "string",
1851
+ enum: [
1852
+ "model",
1853
+ "actor",
1854
+ "feature"
1855
+ ]
1856
+ },
1857
+ from: day,
1858
+ to: day
1859
+ }
1860
+ },
1861
+ async run(input) {
1862
+ const spend = await dashboard.spend(rangeOf(input));
1863
+ switch (input.by) {
1864
+ case "model":
1865
+ return {
1866
+ items: spend.byModel
1867
+ };
1868
+ case "actor":
1869
+ return {
1870
+ items: spend.byActor
1871
+ };
1872
+ case "feature":
1873
+ return {
1874
+ items: spend.byFeature ?? []
1875
+ };
1876
+ default:
1877
+ return spend;
1878
+ }
1879
+ }
1880
+ },
1881
+ {
1882
+ name: "agent_get_tool_stats",
1883
+ kind: "read",
1884
+ description: "Per tool: calls, failures, rejections and latency, over UTC days from/to (default the last 30 days).",
1885
+ input: {
1886
+ type: "object",
1887
+ properties: {
1888
+ from: day,
1889
+ to: day
1890
+ }
1891
+ },
1892
+ async run(input) {
1893
+ return {
1894
+ items: await dashboard.toolStats(rangeOf(input))
1895
+ };
1896
+ }
1897
+ },
1898
+ {
1899
+ name: "agent_get_reliability",
1900
+ kind: "read",
1901
+ description: "Run metrics, by-agent and by-error breakdowns and the trend, over UTC days from/to (default the last 30 days).",
1902
+ input: {
1903
+ type: "object",
1904
+ properties: {
1905
+ from: day,
1906
+ to: day
1907
+ }
1908
+ },
1909
+ async run(input) {
1910
+ return dashboard.reliability(rangeOf(input));
1911
+ }
1912
+ },
1913
+ {
1914
+ name: "agent_list_pending_approvals",
1915
+ kind: "read",
1916
+ description: "Tool calls waiting for a human approval, oldest first. Read-only: approve them in the app. Returns { items, next, total }.",
1917
+ input: {
1918
+ type: "object",
1919
+ properties: {
1920
+ toolName: {
1921
+ type: "string"
1922
+ },
1923
+ agent: {
1924
+ type: "string",
1925
+ description: "Agent name."
1926
+ },
1927
+ actor: {
1928
+ type: "string",
1929
+ description: "Actor ref of the thread owner."
1930
+ },
1931
+ threadId: {
1932
+ type: "string"
1933
+ },
1934
+ from: day,
1935
+ to: day,
1936
+ page,
1937
+ limit
1938
+ }
1939
+ },
1940
+ async run(input) {
1941
+ return listOf(await dashboard.approvalsPage({
1942
+ ...pageOf(input),
1943
+ ...where({
1944
+ toolName: str(input.toolName),
1945
+ agentName: str(input.agent),
1946
+ actorRef: str(input.actor),
1947
+ threadId: str(input.threadId),
1948
+ fromDay: dayOf(input, "from"),
1949
+ toDay: dayOf(input, "to")
1950
+ })
1951
+ }));
1952
+ }
1953
+ },
1954
+ {
1955
+ name: "agent_list_recent_tool_calls",
1956
+ kind: "read",
1957
+ description: "Tool calls of every agent, newest first. Filters: toolName, status, threadId, from/to (UTC days). Returns { items, next, total }.",
1958
+ input: {
1959
+ type: "object",
1960
+ properties: {
1961
+ toolName: {
1962
+ type: "string"
1963
+ },
1964
+ status: {
1965
+ type: "string"
1966
+ },
1967
+ threadId: {
1968
+ type: "string"
1969
+ },
1970
+ from: day,
1971
+ to: day,
1972
+ page,
1973
+ limit
1974
+ }
1975
+ },
1976
+ async run(input) {
1977
+ return listOf(await dashboard.toolCallsPage({
1978
+ ...pageOf(input),
1979
+ ...where({
1980
+ toolName: str(input.toolName),
1981
+ status: str(input.status),
1982
+ threadId: str(input.threadId),
1983
+ fromDay: dayOf(input, "from"),
1984
+ toDay: dayOf(input, "to")
1985
+ })
1986
+ }));
1987
+ }
1988
+ }
1989
+ ];
1990
+ }
1991
+ __name(agentDevTools, "agentDevTools");
1992
+ var logger2 = new import_common9.Logger("AgentDashboardModule");
1993
+ var AgentDevToolsRegistration = class {
1994
+ static {
1995
+ __name(this, "AgentDevToolsRegistration");
1996
+ }
1997
+ enabled;
1998
+ dashboard;
1999
+ access;
2000
+ #unregister;
2001
+ constructor(enabled, dashboard, access) {
2002
+ this.enabled = enabled;
2003
+ this.dashboard = dashboard;
2004
+ this.access = access;
2005
+ }
2006
+ onModuleInit() {
2007
+ if (!this.enabled) return;
2008
+ const slot = devToolsSlot();
2009
+ if (slot.version !== 1 || typeof slot.register !== "function") return;
2010
+ try {
2011
+ this.#unregister = slot.register({
2012
+ lib: "agent",
2013
+ title: "Agent governance",
2014
+ authorize: /* @__PURE__ */ __name((context) => this.access.check(context), "authorize"),
2015
+ tools: agentDevTools(this.dashboard)
2016
+ });
2017
+ } catch (error) {
2018
+ logger2.warn(`agent dev tools not registered: ${error instanceof Error ? error.message : String(error)}`);
2019
+ }
2020
+ }
2021
+ onModuleDestroy() {
2022
+ this.#unregister?.();
2023
+ this.#unregister = void 0;
2024
+ }
2025
+ };
2026
+ AgentDevToolsRegistration = _ts_decorate5([
2027
+ (0, import_common9.Injectable)(),
2028
+ _ts_param5(0, (0, import_common9.Inject)(DASHBOARD_DEV_TOOLS)),
2029
+ _ts_metadata5("design:type", Function),
2030
+ _ts_metadata5("design:paramtypes", [
2031
+ Boolean,
2032
+ typeof DashboardService === "undefined" ? Object : DashboardService,
2033
+ typeof DashboardConsoleAccess === "undefined" ? Object : DashboardConsoleAccess
2034
+ ])
2035
+ ], AgentDevToolsRegistration);
2036
+
2037
+ // src/server/agent-dashboard.module.ts
2038
+ function _ts_decorate6(decorators, target, key, desc) {
2039
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2040
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2041
+ r = Reflect.decorate(decorators, target, key, desc);
2042
+ } else {
2043
+ for (var i = decorators.length - 1; i >= 0; i--) {
2044
+ if (d = decorators[i]) {
2045
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2046
+ }
2047
+ }
2048
+ }
2049
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2050
+ }
2051
+ __name(_ts_decorate6, "_ts_decorate");
1587
2052
  function normalize(path) {
1588
2053
  return normalizeDashboardPath(path);
1589
2054
  }
@@ -1591,8 +2056,8 @@ __name(normalize, "normalize");
1591
2056
  function consoleDecorators(decorators) {
1592
2057
  return [
1593
2058
  ...decorators ?? [],
1594
- (0, import_common9.UseGuards)(ConsoleAuthorizeGuard),
1595
- (0, import_common9.UseFilters)(ConsoleAuthorizeFilter)
2059
+ (0, import_common10.UseGuards)(ConsoleAuthorizeGuard),
2060
+ (0, import_common10.UseFilters)(ConsoleAuthorizeFilter)
1596
2061
  ];
1597
2062
  }
1598
2063
  __name(consoleDecorators, "consoleDecorators");
@@ -1626,7 +2091,12 @@ var AgentApiModule = class _AgentApiModule {
1626
2091
  provide: DASHBOARD_CONSOLE_OPTIONS,
1627
2092
  useValue: consoleOptions
1628
2093
  },
1629
- DashboardConsoleAccess
2094
+ DashboardConsoleAccess,
2095
+ {
2096
+ provide: DASHBOARD_DEV_TOOLS,
2097
+ useValue: options.devTools ?? options.routes !== false
2098
+ },
2099
+ AgentDevToolsRegistration
1630
2100
  ],
1631
2101
  // What a self-mounted `AgentApiController` subclass injects, too (`routes: false`), and the
1632
2102
  // access check `UseConsoleAuthorize()` runs.
@@ -1638,8 +2108,8 @@ var AgentApiModule = class _AgentApiModule {
1638
2108
  };
1639
2109
  }
1640
2110
  };
1641
- AgentApiModule = _ts_decorate5([
1642
- (0, import_common9.Module)({})
2111
+ AgentApiModule = _ts_decorate6([
2112
+ (0, import_common10.Module)({})
1643
2113
  ], AgentApiModule);
1644
2114
  var AgentDashboardModule = class _AgentDashboardModule {
1645
2115
  static {
@@ -1668,10 +2138,13 @@ var AgentDashboardModule = class _AgentDashboardModule {
1668
2138
  } : {},
1669
2139
  ...options.authorize !== void 0 ? {
1670
2140
  authorize: options.authorize
2141
+ } : {},
2142
+ ...options.devTools !== void 0 ? {
2143
+ devTools: options.devTools
1671
2144
  } : {}
1672
2145
  }),
1673
2146
  ...options.routes === false ? [] : [
1674
- import_core2.RouterModule.register([
2147
+ import_core3.RouterModule.register([
1675
2148
  {
1676
2149
  path: basePath,
1677
2150
  module: _AgentDashboardModule
@@ -1706,8 +2179,8 @@ var AgentDashboardModule = class _AgentDashboardModule {
1706
2179
  };
1707
2180
  }
1708
2181
  };
1709
- AgentDashboardModule = _ts_decorate5([
1710
- (0, import_common9.Module)({})
2182
+ AgentDashboardModule = _ts_decorate6([
2183
+ (0, import_common10.Module)({})
1711
2184
  ], AgentDashboardModule);
1712
2185
  // Annotate the CommonJS export names for ESM import in node:
1713
2186
  0 && (module.exports = {
@@ -1719,6 +2192,7 @@ AgentDashboardModule = _ts_decorate5([
1719
2192
  AgentApiController,
1720
2193
  AgentApiModule,
1721
2194
  AgentDashboardModule,
2195
+ AgentDevToolsRegistration,
1722
2196
  AgentUiController,
1723
2197
  CONSOLE_AUTHORIZE_MISSING_WARNING,
1724
2198
  CONSOLE_GATE,
@@ -1731,10 +2205,13 @@ AgentDashboardModule = _ts_decorate5([
1731
2205
  DASHBOARD_APPROVAL_ACTOR_REF,
1732
2206
  DASHBOARD_BASE_PATH,
1733
2207
  DASHBOARD_CONSOLE_OPTIONS,
2208
+ DASHBOARD_DEV_TOOLS,
1734
2209
  DashboardConsoleAccess,
1735
2210
  DashboardService,
1736
2211
  UseConsoleAuthorize,
1737
2212
  agentDashboardMountPaths,
2213
+ agentDevTools,
2214
+ anyAuthorize,
1738
2215
  authorizeByToken,
1739
2216
  normalizeConsoleResult,
1740
2217
  parsePriceInput,