@dudousxd/nestjs-agent-dashboard 0.20.0 → 0.22.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.
@@ -83,6 +83,7 @@ var DASHBOARD_BASE_PATH = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-das
83
83
  var DASHBOARD_API_PATH = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:api-path");
84
84
  var DASHBOARD_APPROVAL_ACTOR_REF = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:approval-actor-ref");
85
85
  var DASHBOARD_CONSOLE_OPTIONS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:console-options");
86
+ var DASHBOARD_DEV_TOOLS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent-dashboard:dev-tools");
86
87
 
87
88
  // src/server/dashboard.service.ts
88
89
  function _ts_decorate(decorators, target, key, desc) {
@@ -161,8 +162,8 @@ var DashboardService = class {
161
162
  };
162
163
  }
163
164
  /** Top threads by cost for a day range (default 10, highest cost first). */
164
- async topThreads(range, limit = 10) {
165
- const rows = await this.queries.spendByThread(range, limit);
165
+ async topThreads(range, limit2 = 10) {
166
+ const rows = await this.queries.spendByThread(range, limit2);
166
167
  return this.withActorLabels(rows);
167
168
  }
168
169
  /** Run reliability for a day range: metrics, by-agent/by-error breakdowns and the trend, in parallel. */
@@ -181,12 +182,12 @@ var DashboardService = class {
181
182
  };
182
183
  }
183
184
  /** Most recent runs (status/agent/duration/error) for the Reliability recent-runs table. */
184
- recentRuns(limit) {
185
- return this.queries.recentRuns(limit);
185
+ recentRuns(limit2) {
186
+ return this.queries.recentRuns(limit2);
186
187
  }
187
188
  /** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */
188
- recentToolCalls(limit) {
189
- return this.queries.recentToolCalls(limit);
189
+ recentToolCalls(limit2) {
190
+ return this.queries.recentToolCalls(limit2);
190
191
  }
191
192
  /** Paged, filterable tool calls for the Runs & tools activity table. */
192
193
  toolCallsPage(query) {
@@ -201,8 +202,8 @@ var DashboardService = class {
201
202
  * Capped and totalless — kept because the SPA still calls `GET approvals`; new callers want
202
203
  * {@link approvalsPage}, which reports how much of the backlog the cap hid.
203
204
  */
204
- pendingApprovals(limit) {
205
- return this.queries.pendingApprovals(limit);
205
+ pendingApprovals(limit2) {
206
+ return this.queries.pendingApprovals(limit2);
206
207
  }
207
208
  /** Paged, filterable approvals inbox with a total — the backlog-safe read of the HITL queue. */
208
209
  approvalsPage(query) {
@@ -290,8 +291,8 @@ var DashboardService = class {
290
291
  });
291
292
  }
292
293
  /** Most recent threads with rolled-up message/token counts. */
293
- async recentThreads(limit) {
294
- const rows = await this.queries.recentThreads(limit);
294
+ async recentThreads(limit2) {
295
+ const rows = await this.queries.recentThreads(limit2);
295
296
  return this.withActorLabels(rows);
296
297
  }
297
298
  /**
@@ -299,10 +300,10 @@ var DashboardService = class {
299
300
  * {@link recentThreads} is.
300
301
  */
301
302
  async threadsPage(query) {
302
- const page = await this.queries.threadsPage(query);
303
- const rows = await this.withActorLabels(page.rows);
303
+ const page2 = await this.queries.threadsPage(query);
304
+ const rows = await this.withActorLabels(page2.rows);
304
305
  return {
305
- ...page,
306
+ ...page2,
306
307
  rows
307
308
  };
308
309
  }
@@ -623,27 +624,27 @@ var AgentApiController = class {
623
624
  return this.dashboard.spend(resolveRange(from, to));
624
625
  }
625
626
  /** Top threads by cost (default 10, max 200) for a day range (defaults to the last 30 days). */
626
- topThreads(from, to, limit) {
627
- return this.dashboard.topThreads(resolveRange(from, to), parseLimit(limit, 10));
627
+ topThreads(from, to, limit2) {
628
+ return this.dashboard.topThreads(resolveRange(from, to), parseLimit(limit2, 10));
628
629
  }
629
630
  /** `{ metrics, byAgent, errors, trend }` for a day range (defaults to the last 30 days). */
630
631
  reliability(from, to) {
631
632
  return this.dashboard.reliability(resolveRange(from, to));
632
633
  }
633
634
  /** Most recent runs (default 50, max 200) for the Reliability recent-runs table. */
634
- runs(limit) {
635
- return this.dashboard.recentRuns(parseLimit(limit, 50));
635
+ runs(limit2) {
636
+ return this.dashboard.recentRuns(parseLimit(limit2, 50));
636
637
  }
637
638
  /**
638
639
  * Paged, filterable runs for the Reliability recent-runs table. `page` (default 1), `limit`
639
640
  * (default 25, max 200), and `where[agentName]`/`where[status]`/`where[errorCode]`/
640
641
  * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
641
642
  */
642
- runsPage(page, limit, where) {
643
- const parsedWhere = parseRunWhere(where);
643
+ runsPage(page2, limit2, where2) {
644
+ const parsedWhere = parseRunWhere(where2);
644
645
  return this.dashboard.runsPage({
645
- page: parsePageNumber(page),
646
- pageSize: parseLimit(limit, 25),
646
+ page: parsePageNumber(page2),
647
+ pageSize: parseLimit(limit2, 25),
647
648
  ...parsedWhere !== void 0 ? {
648
649
  where: parsedWhere
649
650
  } : {}
@@ -660,19 +661,19 @@ var AgentApiController = class {
660
661
  return this.dashboard.runDetail(runId);
661
662
  }
662
663
  /** Most recent tool calls (default 50, max 200) for the activity feed. */
663
- toolCalls(limit) {
664
- return this.dashboard.recentToolCalls(parseLimit(limit, 50));
664
+ toolCalls(limit2) {
665
+ return this.dashboard.recentToolCalls(parseLimit(limit2, 50));
665
666
  }
666
667
  /**
667
668
  * Paged, filterable tool calls for the Runs & tools activity table. `page` (default 1), `limit`
668
669
  * (default 25, max 200), and `where[toolName]`/`where[toolType]`/`where[status]`/`where[threadId]`/
669
670
  * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
670
671
  */
671
- toolCallsPage(page, limit, where) {
672
- const parsedWhere = parseToolCallWhere(where);
672
+ toolCallsPage(page2, limit2, where2) {
673
+ const parsedWhere = parseToolCallWhere(where2);
673
674
  return this.dashboard.toolCallsPage({
674
- page: parsePageNumber(page),
675
- pageSize: parseLimit(limit, 25),
675
+ page: parsePageNumber(page2),
676
+ pageSize: parseLimit(limit2, 25),
676
677
  ...parsedWhere !== void 0 ? {
677
678
  where: parsedWhere
678
679
  } : {}
@@ -684,8 +685,8 @@ var AgentApiController = class {
684
685
  * Capped with NO total, so a backlog past `limit` is invisible from the response alone. Kept
685
686
  * because the SPA still calls it; anything new should call `approvals-page` instead.
686
687
  */
687
- approvals(limit) {
688
- return this.dashboard.pendingApprovals(parseLimit(limit, 50));
688
+ approvals(limit2) {
689
+ return this.dashboard.pendingApprovals(parseLimit(limit2, 50));
689
690
  }
690
691
  /**
691
692
  * Paged, filterable approvals inbox, oldest first. `page` (default 1), `limit` (default 25, max
@@ -695,11 +696,11 @@ var AgentApiController = class {
695
696
  * Unlike `GET approvals` this reports `total`, so the console can say "showing N of M" instead of
696
697
  * silently hiding pending decisions behind a row cap.
697
698
  */
698
- approvalsPage(page, limit, where) {
699
- const parsedWhere = parseApprovalWhere(where);
699
+ approvalsPage(page2, limit2, where2) {
700
+ const parsedWhere = parseApprovalWhere(where2);
700
701
  return this.dashboard.approvalsPage({
701
- page: parsePageNumber(page),
702
- pageSize: parseLimit(limit, 25),
702
+ page: parsePageNumber(page2),
703
+ pageSize: parseLimit(limit2, 25),
703
704
  ...parsedWhere !== void 0 ? {
704
705
  where: parsedWhere
705
706
  } : {}
@@ -746,19 +747,19 @@ var AgentApiController = class {
746
747
  return this.dashboard.genuiStats(resolveRange(from, to));
747
748
  }
748
749
  /** Most recent threads (default 50, max 200) with rolled-up counts. */
749
- threads(limit) {
750
- return this.dashboard.recentThreads(parseLimit(limit, 50));
750
+ threads(limit2) {
751
+ return this.dashboard.recentThreads(parseLimit(limit2, 50));
751
752
  }
752
753
  /**
753
754
  * Paged, filterable threads for the Runs & tools threads table. `page` (default 1), `limit`
754
755
  * (default 25, max 200), and `where[actorRef]`/`where[title]` (case-insensitive substring)/
755
756
  * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
756
757
  */
757
- threadsPage(page, limit, where) {
758
- const parsedWhere = parseThreadWhere(where);
758
+ threadsPage(page2, limit2, where2) {
759
+ const parsedWhere = parseThreadWhere(where2);
759
760
  return this.dashboard.threadsPage({
760
- page: parsePageNumber(page),
761
- pageSize: parseLimit(limit, 25),
761
+ page: parsePageNumber(page2),
762
+ pageSize: parseLimit(limit2, 25),
762
763
  ...parsedWhere !== void 0 ? {
763
764
  where: parsedWhere
764
765
  } : {}
@@ -1051,24 +1052,28 @@ import { RouterModule } from "@nestjs/core";
1051
1052
  import { existsSync, readFileSync } from "fs";
1052
1053
  import { basename, extname, join, resolve, sep } from "path";
1053
1054
  import { fileURLToPath } from "url";
1054
- import { CanAnyone as CanAnyone2 } from "@dudousxd/nestjs-agent-core";
1055
- import { Controller as Controller2, Get as Get2, Header, Inject as Inject4, NotFoundException as NotFoundException2, Param as Param2, Req as Req2, Res, StreamableFile } from "@nestjs/common";
1055
+ import { CanAnyone as CanAnyone3 } from "@dudousxd/nestjs-agent-core";
1056
+ import { Controller as Controller3, Get as Get3, Header as Header2, Inject as Inject5, NotFoundException as NotFoundException2, Param as Param2, Req as Req2, Res, StreamableFile } from "@nestjs/common";
1056
1057
 
1057
1058
  // src/server/console-authorize.guard.ts
1058
1059
  import { Catch, HttpException, Inject as Inject3, Injectable as Injectable2, SetMetadata, UseFilters, UseGuards, applyDecorators } from "@nestjs/common";
1059
- import { HttpAdapterHost, ModuleRef } from "@nestjs/core";
1060
+ import { HttpAdapterHost, ModuleRef as ModuleRef2 } from "@nestjs/core";
1060
1061
 
1061
1062
  // src/server/console-authorize.ts
1062
1063
  import { createHash, timingSafeEqual } from "crypto";
1063
1064
  import { Logger } from "@nestjs/common";
1065
+ import { ModuleRef } from "@nestjs/core";
1064
1066
  var CONSOLE_GATE = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:console-gate");
1065
1067
  var PACKAGE = "@dudousxd/nestjs-agent-dashboard";
1066
1068
  var MODULE = "AgentDashboardModule";
1067
1069
  var logger = new Logger(MODULE);
1068
1070
  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().`;
1069
- var allow = /* @__PURE__ */ __name(() => ({
1071
+ var allow = /* @__PURE__ */ __name((caller) => caller !== void 0 && caller !== "" ? {
1072
+ allowed: true,
1073
+ caller
1074
+ } : {
1070
1075
  allowed: true
1071
- }), "allow");
1076
+ }, "allow");
1072
1077
  var forbidden = /* @__PURE__ */ __name(() => ({
1073
1078
  allowed: false,
1074
1079
  reason: "forbidden"
@@ -1092,7 +1097,9 @@ function normalizeConsoleResult(result) {
1092
1097
  redirectTo: value.redirectTo
1093
1098
  };
1094
1099
  }
1095
- if (value.allowed === true) return allow();
1100
+ if (value.allowed === true) {
1101
+ return allow(typeof value.caller === "string" ? value.caller : void 0);
1102
+ }
1096
1103
  return value.reason === "unauthenticated" ? unauthenticated() : forbidden();
1097
1104
  }
1098
1105
  __name(normalizeConsoleResult, "normalizeConsoleResult");
@@ -1168,7 +1175,7 @@ async function resolveConsoleAuthorize(authorize, moduleRef, options = {}) {
1168
1175
  if (authorize === void 0) {
1169
1176
  if (options.warnIfMissing !== false) log.warn(CONSOLE_AUTHORIZE_MISSING_WARNING);
1170
1177
  const production = isProduction();
1171
- return async () => production ? forbidden() : allow();
1178
+ return async () => production ? forbidden() : allow("dev-open");
1172
1179
  }
1173
1180
  const instanceOf = classInstances(moduleRef);
1174
1181
  let decide;
@@ -1218,12 +1225,18 @@ function authorizeByToken(options) {
1218
1225
  throw new Error("authorizeByToken: `token` is empty; set it (e.g. from an env var).");
1219
1226
  }
1220
1227
  const expected = sha256(token);
1228
+ const caller = `token:${expected.toString("hex").slice(0, 12)}`;
1221
1229
  return (context) => {
1222
1230
  const req = context.switchToHttp().getRequest();
1223
1231
  const header = req?.headers?.authorization;
1224
1232
  const value = Array.isArray(header) ? header[0] : header;
1225
1233
  const match = typeof value === "string" ? /^Bearer\s+(.+)$/i.exec(value.trim()) : null;
1226
- if (match?.[1] !== void 0 && timingSafeEqual(sha256(match[1]), expected)) return true;
1234
+ if (match?.[1] !== void 0 && timingSafeEqual(sha256(match[1]), expected)) {
1235
+ return {
1236
+ allowed: true,
1237
+ caller
1238
+ };
1239
+ }
1227
1240
  return {
1228
1241
  allowed: false,
1229
1242
  reason: "unauthenticated"
@@ -1231,6 +1244,48 @@ function authorizeByToken(options) {
1231
1244
  };
1232
1245
  }
1233
1246
  __name(authorizeByToken, "authorizeByToken");
1247
+ function anyAuthorize(...forms) {
1248
+ if (forms.length === 0) throw new Error("anyAuthorize: pass at least one authorize form.");
1249
+ let AnyConsoleAuthorizer = class AnyConsoleAuthorizer {
1250
+ static {
1251
+ __name(this, "AnyConsoleAuthorizer");
1252
+ }
1253
+ moduleRef;
1254
+ #decides;
1255
+ constructor(moduleRef) {
1256
+ this.moduleRef = moduleRef;
1257
+ }
1258
+ async authorize(context) {
1259
+ this.#decides ??= Promise.all(forms.map((form) => resolveConsoleAuthorize(form, this.moduleRef, {
1260
+ warnIfMissing: false
1261
+ })));
1262
+ this.#decides.catch(() => {
1263
+ this.#decides = void 0;
1264
+ });
1265
+ let unauthenticated2;
1266
+ for (const decide of await this.#decides) {
1267
+ const decision = await decide(context);
1268
+ if (decision.allowed) return decision;
1269
+ if (decision.reason === "unauthenticated") unauthenticated2 ??= decision;
1270
+ }
1271
+ if (unauthenticated2 === void 0) return {
1272
+ allowed: false,
1273
+ reason: "forbidden"
1274
+ };
1275
+ return "redirectTo" in unauthenticated2 && unauthenticated2.redirectTo !== void 0 ? {
1276
+ redirectTo: unauthenticated2.redirectTo
1277
+ } : {
1278
+ allowed: false,
1279
+ reason: "unauthenticated"
1280
+ };
1281
+ }
1282
+ };
1283
+ Reflect.defineMetadata("design:paramtypes", [
1284
+ ModuleRef
1285
+ ], AnyConsoleAuthorizer);
1286
+ return AnyConsoleAuthorizer;
1287
+ }
1288
+ __name(anyAuthorize, "anyAuthorize");
1234
1289
 
1235
1290
  // src/server/console-authorize.guard.ts
1236
1291
  function _ts_decorate3(decorators, target, key, desc) {
@@ -1288,7 +1343,7 @@ DashboardConsoleAccess = _ts_decorate3([
1288
1343
  _ts_metadata3("design:type", Function),
1289
1344
  _ts_metadata3("design:paramtypes", [
1290
1345
  typeof DashboardConsoleOptions === "undefined" ? Object : DashboardConsoleOptions,
1291
- typeof ModuleRef === "undefined" ? Object : ModuleRef
1346
+ typeof ModuleRef2 === "undefined" ? Object : ModuleRef2
1292
1347
  ])
1293
1348
  ], DashboardConsoleAccess);
1294
1349
  var CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-agent-dashboard:console-page";
@@ -1299,7 +1354,7 @@ var ConsoleAccessDenied = class extends HttpException {
1299
1354
  }
1300
1355
  decision;
1301
1356
  page;
1302
- constructor(decision, page) {
1357
+ constructor(decision, page2) {
1303
1358
  super(decision.reason === "forbidden" ? {
1304
1359
  error: "forbidden"
1305
1360
  } : {
@@ -1307,13 +1362,13 @@ var ConsoleAccessDenied = class extends HttpException {
1307
1362
  ...decision.redirectTo !== void 0 ? {
1308
1363
  redirectTo: decision.redirectTo
1309
1364
  } : {}
1310
- }, decision.reason === "forbidden" ? 403 : 401), this.decision = decision, this.page = page;
1365
+ }, decision.reason === "forbidden" ? 403 : 401), this.decision = decision, this.page = page2;
1311
1366
  }
1312
1367
  };
1313
1368
  function isPageNavigation(context) {
1314
1369
  if (context.getType() !== "http") return false;
1315
- const page = Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getHandler()) === true || Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) === true;
1316
- if (!page) return false;
1370
+ const page2 = Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getHandler()) === true || Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) === true;
1371
+ if (!page2) return false;
1317
1372
  const req = context.switchToHttp().getRequest();
1318
1373
  if ((req.method ?? "GET").toUpperCase() !== "GET") return false;
1319
1374
  const accept = req.headers?.accept;
@@ -1360,12 +1415,12 @@ var ConsoleAuthorizeFilter = class {
1360
1415
  catch(exception, host) {
1361
1416
  const adapter = this.adapterHost.httpAdapter;
1362
1417
  const res = host.switchToHttp().getResponse();
1363
- const { decision, page } = exception;
1364
- if (page && decision.reason === "unauthenticated" && decision.redirectTo !== void 0) {
1418
+ const { decision, page: page2 } = exception;
1419
+ if (page2 && decision.reason === "unauthenticated" && decision.redirectTo !== void 0) {
1365
1420
  adapter.redirect(res, 302, decision.redirectTo);
1366
1421
  return;
1367
1422
  }
1368
- if (page) {
1423
+ if (page2) {
1369
1424
  const forbidden2 = decision.reason === "forbidden";
1370
1425
  adapter.setHeader(res, "Content-Type", "text/html; charset=utf-8");
1371
1426
  adapter.reply(res, forbidden2 ? FORBIDDEN_PAGE : UNAUTHENTICATED_PAGE, forbidden2 ? 403 : 401);
@@ -1387,7 +1442,9 @@ function UseConsoleAuthorize() {
1387
1442
  }
1388
1443
  __name(UseConsoleAuthorize, "UseConsoleAuthorize");
1389
1444
 
1390
- // src/server/agent-ui.controller.ts
1445
+ // src/server/console-switcher.ts
1446
+ import { CanAnyone as CanAnyone2 } from "@dudousxd/nestjs-agent-core";
1447
+ import { Controller as Controller2, Get as Get2, Header, Inject as Inject4, Injectable as Injectable3, Logger as Logger2, createParamDecorator } from "@nestjs/common";
1391
1448
  function _ts_decorate4(decorators, target, key, desc) {
1392
1449
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1393
1450
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -1414,7 +1471,186 @@ function _ts_param4(paramIndex, decorator) {
1414
1471
  };
1415
1472
  }
1416
1473
  __name(_ts_param4, "_ts_param");
1474
+ var AVIARY_CONSOLES_SLOT = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:consoles");
1475
+ var ID = /^[a-z][a-z0-9-]*$/;
1476
+ var ICONS = /* @__PURE__ */ new Set([
1477
+ "workflow",
1478
+ "telescope",
1479
+ "media",
1480
+ "payments",
1481
+ "agent",
1482
+ "identity",
1483
+ "generic"
1484
+ ]);
1485
+ var CONSOLE_PROBE_TIMEOUT_MS = 300;
1486
+ function isSameOriginPath(path) {
1487
+ return typeof path === "string" && path.startsWith("/") && !path.startsWith("//");
1488
+ }
1489
+ __name(isSameOriginPath, "isSameOriginPath");
1490
+ function readAviaryConsoles() {
1491
+ const value = globalThis[AVIARY_CONSOLES_SLOT];
1492
+ if (value?.version !== 1 || !Array.isArray(value.entries)) return void 0;
1493
+ return value;
1494
+ }
1495
+ __name(readAviaryConsoles, "readAviaryConsoles");
1496
+ function registerAviaryConsole(entry, logger4) {
1497
+ if (!ID.test(entry.id)) throw new Error(`console id ${JSON.stringify(entry.id)} is invalid`);
1498
+ if (!isSameOriginPath(entry.path)) {
1499
+ throw new Error(`console path ${JSON.stringify(entry.path)} must start with a single "/"`);
1500
+ }
1501
+ const holder = globalThis;
1502
+ if (holder[AVIARY_CONSOLES_SLOT] === void 0) {
1503
+ const fresh = {
1504
+ version: 1,
1505
+ entries: []
1506
+ };
1507
+ holder[AVIARY_CONSOLES_SLOT] = fresh;
1508
+ }
1509
+ const registry = readAviaryConsoles();
1510
+ if (registry === void 0) return void 0;
1511
+ if (registry.entries.some((existing) => existing.id === entry.id)) {
1512
+ logger4?.warn(`console id "${entry.id}" is already registered`);
1513
+ return void 0;
1514
+ }
1515
+ registry.entries.push(entry);
1516
+ return () => {
1517
+ const index = registry.entries.indexOf(entry);
1518
+ if (index !== -1) registry.entries.splice(index, 1);
1519
+ };
1520
+ }
1521
+ __name(registerAviaryConsole, "registerAviaryConsole");
1522
+ function consoleSwitcherConfig(selfId, endpoint) {
1523
+ const registry = readAviaryConsoles();
1524
+ if (!registry?.entries.some((entry) => entry.id === selfId)) return void 0;
1525
+ const others = registry.entries.filter((entry) => entry.id !== selfId).length;
1526
+ return others > 0 ? {
1527
+ endpoint,
1528
+ others
1529
+ } : void 0;
1530
+ }
1531
+ __name(consoleSwitcherConfig, "consoleSwitcherConfig");
1532
+ async function allows(entry, context, ms) {
1533
+ let timer;
1534
+ const timeout = new Promise((resolve2) => {
1535
+ timer = setTimeout(() => resolve2(false), ms);
1536
+ });
1537
+ try {
1538
+ return await Promise.race([
1539
+ Promise.resolve().then(() => entry.authorize(context)).then((decision) => decision?.allowed === true, () => false),
1540
+ timeout
1541
+ ]);
1542
+ } finally {
1543
+ clearTimeout(timer);
1544
+ }
1545
+ }
1546
+ __name(allows, "allows");
1547
+ async function listVisibleConsoles(registry, selfId, context, timeoutMs = CONSOLE_PROBE_TIMEOUT_MS) {
1548
+ const others = (registry?.entries ?? []).filter((entry) => entry.id !== selfId && isSameOriginPath(entry.path)).sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
1549
+ const verdicts = await Promise.all(others.map((entry) => allows(entry, context, timeoutMs)));
1550
+ return {
1551
+ version: 1,
1552
+ current: selfId,
1553
+ consoles: others.filter((_, index) => verdicts[index]).map((entry) => ({
1554
+ id: entry.id,
1555
+ title: entry.title,
1556
+ icon: ICONS.has(entry.icon) ? entry.icon : "generic",
1557
+ href: entry.path
1558
+ }))
1559
+ };
1560
+ }
1561
+ __name(listVisibleConsoles, "listVisibleConsoles");
1562
+ var AGENT_CONSOLE_ID = "agent";
1563
+ var logger2 = new Logger2("AgentDashboardModule");
1564
+ var AgentConsoleRegistration = class {
1565
+ static {
1566
+ __name(this, "AgentConsoleRegistration");
1567
+ }
1568
+ basePath;
1569
+ access;
1570
+ #unregister;
1571
+ constructor(basePath, access) {
1572
+ this.basePath = basePath;
1573
+ this.access = access;
1574
+ }
1575
+ onModuleInit() {
1576
+ this.#unregister = registerAviaryConsole({
1577
+ id: AGENT_CONSOLE_ID,
1578
+ title: "Agents",
1579
+ icon: "agent",
1580
+ order: 30,
1581
+ path: this.basePath === "" ? "/" : this.basePath,
1582
+ authorize: /* @__PURE__ */ __name((context) => this.access.check(context), "authorize")
1583
+ }, logger2);
1584
+ }
1585
+ onModuleDestroy() {
1586
+ this.#unregister?.();
1587
+ this.#unregister = void 0;
1588
+ }
1589
+ };
1590
+ AgentConsoleRegistration = _ts_decorate4([
1591
+ Injectable3(),
1592
+ _ts_param4(0, Inject4(DASHBOARD_BASE_PATH)),
1593
+ _ts_metadata4("design:type", Function),
1594
+ _ts_metadata4("design:paramtypes", [
1595
+ String,
1596
+ typeof DashboardConsoleAccess === "undefined" ? Object : DashboardConsoleAccess
1597
+ ])
1598
+ ], AgentConsoleRegistration);
1599
+ var CurrentExecutionContext = createParamDecorator((_data, context) => ({
1600
+ context
1601
+ }));
1602
+ var AgentConsolesController = class {
1603
+ static {
1604
+ __name(this, "AgentConsolesController");
1605
+ }
1606
+ consoles(current) {
1607
+ return listVisibleConsoles(readAviaryConsoles(), AGENT_CONSOLE_ID, current.context);
1608
+ }
1609
+ };
1610
+ _ts_decorate4([
1611
+ Get2("consoles"),
1612
+ Header("Cache-Control", "private, no-store"),
1613
+ _ts_param4(0, CurrentExecutionContext()),
1614
+ _ts_metadata4("design:type", Function),
1615
+ _ts_metadata4("design:paramtypes", [
1616
+ typeof CurrentContext === "undefined" ? Object : CurrentContext
1617
+ ]),
1618
+ _ts_metadata4("design:returntype", typeof Promise === "undefined" ? Object : Promise)
1619
+ ], AgentConsolesController.prototype, "consoles", null);
1620
+ AgentConsolesController = _ts_decorate4([
1621
+ CanAnyone2(),
1622
+ Controller2()
1623
+ ], AgentConsolesController);
1624
+
1625
+ // src/server/agent-ui.controller.ts
1626
+ function _ts_decorate5(decorators, target, key, desc) {
1627
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1628
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1629
+ r = Reflect.decorate(decorators, target, key, desc);
1630
+ } else {
1631
+ for (var i = decorators.length - 1; i >= 0; i--) {
1632
+ if (d = decorators[i]) {
1633
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1634
+ }
1635
+ }
1636
+ }
1637
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1638
+ }
1639
+ __name(_ts_decorate5, "_ts_decorate");
1640
+ function _ts_metadata5(metadataKey, metadataValue) {
1641
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
1642
+ return Reflect.metadata(metadataKey, metadataValue);
1643
+ }
1644
+ }
1645
+ __name(_ts_metadata5, "_ts_metadata");
1646
+ function _ts_param5(paramIndex, decorator) {
1647
+ return function(target, key) {
1648
+ decorator(target, key, paramIndex);
1649
+ };
1650
+ }
1651
+ __name(_ts_param5, "_ts_param");
1417
1652
  var BUILD_BASE = "/ai-gateway";
1653
+ var AGENT_DASHBOARD_CONFIG_ID = "agent-dashboard-config";
1418
1654
  function spaDir() {
1419
1655
  return fileURLToPath(new URL("../spa", import.meta.url));
1420
1656
  }
@@ -1452,9 +1688,23 @@ var AgentUiController = class {
1452
1688
  throw new NotFoundException2("Dashboard is not built. Run the package build.");
1453
1689
  }
1454
1690
  const html = readFileSync(indexPath, "utf8").replaceAll(`="${BUILD_BASE}/`, `="${this.basePath}/`);
1455
- const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script>`;
1691
+ const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script>${this.configBlock()}`;
1456
1692
  return html.includes("</head>") ? html.replace("</head>", `${inject}</head>`) : inject + html;
1457
1693
  }
1694
+ /**
1695
+ * The page's JSON config block (`agent-dashboard-config`, CSP-safe: data, not script). It carries
1696
+ * `switcher` only when another Aviary console is registered.
1697
+ */
1698
+ configBlock() {
1699
+ const switcher = consoleSwitcherConfig(AGENT_CONSOLE_ID, `${this.apiBasePath}/consoles`);
1700
+ const json = JSON.stringify({
1701
+ apiBase: this.apiBasePath,
1702
+ ...switcher !== void 0 ? {
1703
+ switcher
1704
+ } : {}
1705
+ }).replaceAll("<", "\\u003c");
1706
+ return `<script type="application/json" id="${AGENT_DASHBOARD_CONFIG_ID}">${json}</script>`;
1707
+ }
1458
1708
  asset(file) {
1459
1709
  const safe = basename(file);
1460
1710
  if (safe !== file) throw new NotFoundException2();
@@ -1469,46 +1719,452 @@ var AgentUiController = class {
1469
1719
  });
1470
1720
  }
1471
1721
  };
1472
- _ts_decorate4([
1473
- Get2(),
1722
+ _ts_decorate5([
1723
+ Get3(),
1474
1724
  ConsolePage(),
1475
- Header("Content-Type", "text/html; charset=utf-8"),
1476
- Header("Cache-Control", "no-store, must-revalidate"),
1477
- _ts_param4(0, Req2()),
1478
- _ts_param4(1, Res({
1725
+ Header2("Content-Type", "text/html; charset=utf-8"),
1726
+ Header2("Cache-Control", "no-store, must-revalidate"),
1727
+ _ts_param5(0, Req2()),
1728
+ _ts_param5(1, Res({
1479
1729
  passthrough: true
1480
1730
  })),
1481
- _ts_metadata4("design:type", Function),
1482
- _ts_metadata4("design:paramtypes", [
1731
+ _ts_metadata5("design:type", Function),
1732
+ _ts_metadata5("design:paramtypes", [
1483
1733
  typeof UiPageRequest === "undefined" ? Object : UiPageRequest,
1484
1734
  typeof UiPageResponse === "undefined" ? Object : UiPageResponse
1485
1735
  ]),
1486
- _ts_metadata4("design:returntype", String)
1736
+ _ts_metadata5("design:returntype", String)
1487
1737
  ], AgentUiController.prototype, "index", null);
1488
- _ts_decorate4([
1489
- Get2("assets/:file"),
1490
- Header("Cache-Control", "public, max-age=31536000, immutable"),
1491
- _ts_param4(0, Param2("file")),
1492
- _ts_metadata4("design:type", Function),
1493
- _ts_metadata4("design:paramtypes", [
1738
+ _ts_decorate5([
1739
+ Get3("assets/:file"),
1740
+ Header2("Cache-Control", "public, max-age=31536000, immutable"),
1741
+ _ts_param5(0, Param2("file")),
1742
+ _ts_metadata5("design:type", Function),
1743
+ _ts_metadata5("design:paramtypes", [
1494
1744
  String
1495
1745
  ]),
1496
- _ts_metadata4("design:returntype", typeof StreamableFile === "undefined" ? Object : StreamableFile)
1746
+ _ts_metadata5("design:returntype", typeof StreamableFile === "undefined" ? Object : StreamableFile)
1497
1747
  ], AgentUiController.prototype, "asset", null);
1498
- AgentUiController = _ts_decorate4([
1499
- CanAnyone2(),
1500
- Controller2(),
1501
- _ts_param4(0, Inject4(DASHBOARD_BASE_PATH)),
1502
- _ts_param4(1, Inject4(DASHBOARD_API_PATH)),
1503
- _ts_metadata4("design:type", Function),
1504
- _ts_metadata4("design:paramtypes", [
1748
+ AgentUiController = _ts_decorate5([
1749
+ CanAnyone3(),
1750
+ Controller3(),
1751
+ _ts_param5(0, Inject5(DASHBOARD_BASE_PATH)),
1752
+ _ts_param5(1, Inject5(DASHBOARD_API_PATH)),
1753
+ _ts_metadata5("design:type", Function),
1754
+ _ts_metadata5("design:paramtypes", [
1505
1755
  String,
1506
1756
  String
1507
1757
  ])
1508
1758
  ], AgentUiController);
1509
1759
 
1760
+ // src/server/dev-tools.ts
1761
+ import { Inject as Inject6, Injectable as Injectable4, Logger as Logger3, NotFoundException as NotFoundException3 } from "@nestjs/common";
1762
+ function _ts_decorate6(decorators, target, key, desc) {
1763
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1764
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1765
+ r = Reflect.decorate(decorators, target, key, desc);
1766
+ } else {
1767
+ for (var i = decorators.length - 1; i >= 0; i--) {
1768
+ if (d = decorators[i]) {
1769
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1770
+ }
1771
+ }
1772
+ }
1773
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1774
+ }
1775
+ __name(_ts_decorate6, "_ts_decorate");
1776
+ function _ts_metadata6(metadataKey, metadataValue) {
1777
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
1778
+ return Reflect.metadata(metadataKey, metadataValue);
1779
+ }
1780
+ }
1781
+ __name(_ts_metadata6, "_ts_metadata");
1782
+ function _ts_param6(paramIndex, decorator) {
1783
+ return function(target, key) {
1784
+ decorator(target, key, paramIndex);
1785
+ };
1786
+ }
1787
+ __name(_ts_param6, "_ts_param");
1788
+ var DEV_TOOLS_SLOT = /* @__PURE__ */ Symbol.for("@dudousxd/aviary:dev-tools");
1789
+ function devToolsSlot() {
1790
+ const holder = globalThis;
1791
+ const existing = holder[DEV_TOOLS_SLOT];
1792
+ if (existing !== void 0) return existing;
1793
+ const libs = [];
1794
+ const slot = {
1795
+ version: 1,
1796
+ libs,
1797
+ register(lib) {
1798
+ if (libs.some((entry) => entry.lib === lib.lib)) {
1799
+ throw new Error(`dev tools: the lib "${lib.lib}" is already registered`);
1800
+ }
1801
+ libs.push(lib);
1802
+ return () => {
1803
+ const index = libs.indexOf(lib);
1804
+ if (index !== -1) libs.splice(index, 1);
1805
+ };
1806
+ }
1807
+ };
1808
+ holder[DEV_TOOLS_SLOT] = slot;
1809
+ return slot;
1810
+ }
1811
+ __name(devToolsSlot, "devToolsSlot");
1812
+ var DEFAULT_LIMIT = 20;
1813
+ var MAX_LIMIT = 100;
1814
+ var MESSAGE_LIMIT = 50;
1815
+ var MESSAGE_BYTES = 2 * 1024;
1816
+ var DAY_MS2 = 864e5;
1817
+ var ISO_DAY3 = /^\d{4}-\d{2}-\d{2}$/;
1818
+ var day = {
1819
+ type: "string",
1820
+ description: "UTC day, YYYY-MM-DD."
1821
+ };
1822
+ var page = {
1823
+ type: "integer",
1824
+ description: "Page, from 1. Pass the `next` of the previous page.",
1825
+ minimum: 1
1826
+ };
1827
+ var limit = {
1828
+ type: "integer",
1829
+ description: `Rows per page (default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}).`,
1830
+ minimum: 1,
1831
+ maximum: MAX_LIMIT
1832
+ };
1833
+ var str = /* @__PURE__ */ __name((value) => typeof value === "string" && value !== "" ? value : void 0, "str");
1834
+ function dayOf(input, key) {
1835
+ const value = str(input[key]);
1836
+ if (value !== void 0 && !ISO_DAY3.test(value)) throw new Error(`"${key}" must be YYYY-MM-DD`);
1837
+ return value;
1838
+ }
1839
+ __name(dayOf, "dayOf");
1840
+ function rangeOf(input) {
1841
+ const utcDay2 = /* @__PURE__ */ __name((daysAgo) => new Date(Date.now() - daysAgo * DAY_MS2).toISOString().slice(0, 10), "utcDay");
1842
+ return {
1843
+ fromDay: dayOf(input, "from") ?? utcDay2(29),
1844
+ toDay: dayOf(input, "to") ?? utcDay2(0)
1845
+ };
1846
+ }
1847
+ __name(rangeOf, "rangeOf");
1848
+ function pageOf(input) {
1849
+ return {
1850
+ page: typeof input.page === "number" ? input.page : 1,
1851
+ pageSize: typeof input.limit === "number" ? input.limit : DEFAULT_LIMIT
1852
+ };
1853
+ }
1854
+ __name(pageOf, "pageOf");
1855
+ function where(fields) {
1856
+ const set = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== void 0));
1857
+ return Object.keys(set).length > 0 ? {
1858
+ where: set
1859
+ } : {};
1860
+ }
1861
+ __name(where, "where");
1862
+ function listOf(result) {
1863
+ return {
1864
+ items: result.rows,
1865
+ next: result.page * result.pageSize < result.total ? result.page + 1 : null,
1866
+ total: result.total
1867
+ };
1868
+ }
1869
+ __name(listOf, "listOf");
1870
+ function cut(text, maxBytes) {
1871
+ const size = Buffer.byteLength(text, "utf8");
1872
+ if (size <= maxBytes) return text;
1873
+ const suffix = `\u2026(truncated, ${size} bytes)`;
1874
+ const keep = Math.max(0, maxBytes - Buffer.byteLength(suffix, "utf8"));
1875
+ let head = Buffer.from(text, "utf8").subarray(0, keep).toString("utf8");
1876
+ if (head.endsWith("\uFFFD")) head = head.slice(0, -1);
1877
+ return `${head}${suffix}`;
1878
+ }
1879
+ __name(cut, "cut");
1880
+ function agentDevTools(dashboard) {
1881
+ return [
1882
+ {
1883
+ name: "agent_list_runs",
1884
+ kind: "read",
1885
+ 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.",
1886
+ input: {
1887
+ type: "object",
1888
+ properties: {
1889
+ agent: {
1890
+ type: "string",
1891
+ description: "Agent name."
1892
+ },
1893
+ status: {
1894
+ type: "string",
1895
+ enum: [
1896
+ "running",
1897
+ "completed",
1898
+ "failed",
1899
+ "cancelled"
1900
+ ]
1901
+ },
1902
+ errorCode: {
1903
+ type: "string"
1904
+ },
1905
+ threadId: {
1906
+ type: "string"
1907
+ },
1908
+ from: day,
1909
+ to: day,
1910
+ page,
1911
+ limit
1912
+ }
1913
+ },
1914
+ async run(input) {
1915
+ return listOf(await dashboard.runsPage({
1916
+ ...pageOf(input),
1917
+ ...where({
1918
+ agentName: str(input.agent),
1919
+ status: str(input.status),
1920
+ errorCode: str(input.errorCode),
1921
+ threadId: str(input.threadId),
1922
+ fromDay: dayOf(input, "from"),
1923
+ toDay: dayOf(input, "to")
1924
+ })
1925
+ }));
1926
+ }
1927
+ },
1928
+ {
1929
+ name: "agent_get_run",
1930
+ kind: "read",
1931
+ 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.",
1932
+ input: {
1933
+ type: "object",
1934
+ properties: {
1935
+ id: {
1936
+ type: "string",
1937
+ description: "Run id."
1938
+ },
1939
+ includeMessages: {
1940
+ type: "boolean",
1941
+ description: "Include messages (default false)."
1942
+ }
1943
+ },
1944
+ required: [
1945
+ "id"
1946
+ ]
1947
+ },
1948
+ async run(input) {
1949
+ let detail;
1950
+ try {
1951
+ detail = await dashboard.runDetail(String(input.id));
1952
+ } catch (error) {
1953
+ if (error instanceof NotFoundException3) throw new Error(`run ${String(input.id)} not found`);
1954
+ throw error;
1955
+ }
1956
+ if (input.includeMessages !== true) return detail;
1957
+ const thread = await dashboard.threadDetail({
1958
+ threadId: detail.run.threadId,
1959
+ messageLimit: MESSAGE_LIMIT,
1960
+ runLimit: 1
1961
+ });
1962
+ return {
1963
+ ...detail,
1964
+ messages: thread.messages.map((message) => ({
1965
+ ...message,
1966
+ content: cut(message.content, MESSAGE_BYTES)
1967
+ }))
1968
+ };
1969
+ }
1970
+ },
1971
+ {
1972
+ name: "agent_get_spend",
1973
+ kind: "read",
1974
+ 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.",
1975
+ input: {
1976
+ type: "object",
1977
+ properties: {
1978
+ by: {
1979
+ type: "string",
1980
+ enum: [
1981
+ "model",
1982
+ "actor",
1983
+ "feature"
1984
+ ]
1985
+ },
1986
+ from: day,
1987
+ to: day
1988
+ }
1989
+ },
1990
+ async run(input) {
1991
+ const spend = await dashboard.spend(rangeOf(input));
1992
+ switch (input.by) {
1993
+ case "model":
1994
+ return {
1995
+ items: spend.byModel
1996
+ };
1997
+ case "actor":
1998
+ return {
1999
+ items: spend.byActor
2000
+ };
2001
+ case "feature":
2002
+ return {
2003
+ items: spend.byFeature ?? []
2004
+ };
2005
+ default:
2006
+ return spend;
2007
+ }
2008
+ }
2009
+ },
2010
+ {
2011
+ name: "agent_get_tool_stats",
2012
+ kind: "read",
2013
+ description: "Per tool: calls, failures, rejections and latency, over UTC days from/to (default the last 30 days).",
2014
+ input: {
2015
+ type: "object",
2016
+ properties: {
2017
+ from: day,
2018
+ to: day
2019
+ }
2020
+ },
2021
+ async run(input) {
2022
+ return {
2023
+ items: await dashboard.toolStats(rangeOf(input))
2024
+ };
2025
+ }
2026
+ },
2027
+ {
2028
+ name: "agent_get_reliability",
2029
+ kind: "read",
2030
+ description: "Run metrics, by-agent and by-error breakdowns and the trend, over UTC days from/to (default the last 30 days).",
2031
+ input: {
2032
+ type: "object",
2033
+ properties: {
2034
+ from: day,
2035
+ to: day
2036
+ }
2037
+ },
2038
+ async run(input) {
2039
+ return dashboard.reliability(rangeOf(input));
2040
+ }
2041
+ },
2042
+ {
2043
+ name: "agent_list_pending_approvals",
2044
+ kind: "read",
2045
+ description: "Tool calls waiting for a human approval, oldest first. Read-only: approve them in the app. Returns { items, next, total }.",
2046
+ input: {
2047
+ type: "object",
2048
+ properties: {
2049
+ toolName: {
2050
+ type: "string"
2051
+ },
2052
+ agent: {
2053
+ type: "string",
2054
+ description: "Agent name."
2055
+ },
2056
+ actor: {
2057
+ type: "string",
2058
+ description: "Actor ref of the thread owner."
2059
+ },
2060
+ threadId: {
2061
+ type: "string"
2062
+ },
2063
+ from: day,
2064
+ to: day,
2065
+ page,
2066
+ limit
2067
+ }
2068
+ },
2069
+ async run(input) {
2070
+ return listOf(await dashboard.approvalsPage({
2071
+ ...pageOf(input),
2072
+ ...where({
2073
+ toolName: str(input.toolName),
2074
+ agentName: str(input.agent),
2075
+ actorRef: str(input.actor),
2076
+ threadId: str(input.threadId),
2077
+ fromDay: dayOf(input, "from"),
2078
+ toDay: dayOf(input, "to")
2079
+ })
2080
+ }));
2081
+ }
2082
+ },
2083
+ {
2084
+ name: "agent_list_recent_tool_calls",
2085
+ kind: "read",
2086
+ description: "Tool calls of every agent, newest first. Filters: toolName, status, threadId, from/to (UTC days). Returns { items, next, total }.",
2087
+ input: {
2088
+ type: "object",
2089
+ properties: {
2090
+ toolName: {
2091
+ type: "string"
2092
+ },
2093
+ status: {
2094
+ type: "string"
2095
+ },
2096
+ threadId: {
2097
+ type: "string"
2098
+ },
2099
+ from: day,
2100
+ to: day,
2101
+ page,
2102
+ limit
2103
+ }
2104
+ },
2105
+ async run(input) {
2106
+ return listOf(await dashboard.toolCallsPage({
2107
+ ...pageOf(input),
2108
+ ...where({
2109
+ toolName: str(input.toolName),
2110
+ status: str(input.status),
2111
+ threadId: str(input.threadId),
2112
+ fromDay: dayOf(input, "from"),
2113
+ toDay: dayOf(input, "to")
2114
+ })
2115
+ }));
2116
+ }
2117
+ }
2118
+ ];
2119
+ }
2120
+ __name(agentDevTools, "agentDevTools");
2121
+ var logger3 = new Logger3("AgentDashboardModule");
2122
+ var AgentDevToolsRegistration = class {
2123
+ static {
2124
+ __name(this, "AgentDevToolsRegistration");
2125
+ }
2126
+ enabled;
2127
+ dashboard;
2128
+ access;
2129
+ #unregister;
2130
+ constructor(enabled, dashboard, access) {
2131
+ this.enabled = enabled;
2132
+ this.dashboard = dashboard;
2133
+ this.access = access;
2134
+ }
2135
+ onModuleInit() {
2136
+ if (!this.enabled) return;
2137
+ const slot = devToolsSlot();
2138
+ if (slot.version !== 1 || typeof slot.register !== "function") return;
2139
+ try {
2140
+ this.#unregister = slot.register({
2141
+ lib: "agent",
2142
+ title: "Agent governance",
2143
+ authorize: /* @__PURE__ */ __name((context) => this.access.check(context), "authorize"),
2144
+ tools: agentDevTools(this.dashboard)
2145
+ });
2146
+ } catch (error) {
2147
+ logger3.warn(`agent dev tools not registered: ${error instanceof Error ? error.message : String(error)}`);
2148
+ }
2149
+ }
2150
+ onModuleDestroy() {
2151
+ this.#unregister?.();
2152
+ this.#unregister = void 0;
2153
+ }
2154
+ };
2155
+ AgentDevToolsRegistration = _ts_decorate6([
2156
+ Injectable4(),
2157
+ _ts_param6(0, Inject6(DASHBOARD_DEV_TOOLS)),
2158
+ _ts_metadata6("design:type", Function),
2159
+ _ts_metadata6("design:paramtypes", [
2160
+ Boolean,
2161
+ typeof DashboardService === "undefined" ? Object : DashboardService,
2162
+ typeof DashboardConsoleAccess === "undefined" ? Object : DashboardConsoleAccess
2163
+ ])
2164
+ ], AgentDevToolsRegistration);
2165
+
1510
2166
  // src/server/agent-dashboard.module.ts
1511
- function _ts_decorate5(decorators, target, key, desc) {
2167
+ function _ts_decorate7(decorators, target, key, desc) {
1512
2168
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1513
2169
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
1514
2170
  r = Reflect.decorate(decorators, target, key, desc);
@@ -1521,7 +2177,7 @@ function _ts_decorate5(decorators, target, key, desc) {
1521
2177
  }
1522
2178
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1523
2179
  }
1524
- __name(_ts_decorate5, "_ts_decorate");
2180
+ __name(_ts_decorate7, "_ts_decorate");
1525
2181
  function normalize(path) {
1526
2182
  return normalizeDashboardPath(path);
1527
2183
  }
@@ -1550,7 +2206,8 @@ var AgentApiModule = class _AgentApiModule {
1550
2206
  ...options.imports ?? []
1551
2207
  ],
1552
2208
  controllers: options.routes === false ? [] : withControllerDecorators([
1553
- AgentApiController
2209
+ AgentApiController,
2210
+ AgentConsolesController
1554
2211
  ], consoleDecorators(options.decorators)),
1555
2212
  providers: [
1556
2213
  DashboardService,
@@ -1564,7 +2221,12 @@ var AgentApiModule = class _AgentApiModule {
1564
2221
  provide: DASHBOARD_CONSOLE_OPTIONS,
1565
2222
  useValue: consoleOptions
1566
2223
  },
1567
- DashboardConsoleAccess
2224
+ DashboardConsoleAccess,
2225
+ {
2226
+ provide: DASHBOARD_DEV_TOOLS,
2227
+ useValue: options.devTools ?? options.routes !== false
2228
+ },
2229
+ AgentDevToolsRegistration
1568
2230
  ],
1569
2231
  // What a self-mounted `AgentApiController` subclass injects, too (`routes: false`), and the
1570
2232
  // access check `UseConsoleAuthorize()` runs.
@@ -1576,7 +2238,7 @@ var AgentApiModule = class _AgentApiModule {
1576
2238
  };
1577
2239
  }
1578
2240
  };
1579
- AgentApiModule = _ts_decorate5([
2241
+ AgentApiModule = _ts_decorate7([
1580
2242
  Module({})
1581
2243
  ], AgentApiModule);
1582
2244
  var AgentDashboardModule = class _AgentDashboardModule {
@@ -1606,6 +2268,9 @@ var AgentDashboardModule = class _AgentDashboardModule {
1606
2268
  } : {},
1607
2269
  ...options.authorize !== void 0 ? {
1608
2270
  authorize: options.authorize
2271
+ } : {},
2272
+ ...options.devTools !== void 0 ? {
2273
+ devTools: options.devTools
1609
2274
  } : {}
1610
2275
  }),
1611
2276
  ...options.routes === false ? [] : [
@@ -1632,7 +2297,11 @@ var AgentDashboardModule = class _AgentDashboardModule {
1632
2297
  {
1633
2298
  provide: DASHBOARD_API_PATH,
1634
2299
  useValue: apiBasePath
1635
- }
2300
+ },
2301
+ // The console switcher lists this console only where it serves its own page.
2302
+ ...options.routes === false ? [] : [
2303
+ AgentConsoleRegistration
2304
+ ]
1636
2305
  ],
1637
2306
  // Re-export the API module so its DashboardService reaches importers (e.g. the host's own
1638
2307
  // controllers), and the paths a self-mounted `AgentUiController` subclass injects.
@@ -1644,22 +2313,29 @@ var AgentDashboardModule = class _AgentDashboardModule {
1644
2313
  };
1645
2314
  }
1646
2315
  };
1647
- AgentDashboardModule = _ts_decorate5([
2316
+ AgentDashboardModule = _ts_decorate7([
1648
2317
  Module({})
1649
2318
  ], AgentDashboardModule);
1650
2319
  export {
1651
2320
  AGENT_ACTOR_DIRECTORY,
1652
2321
  AGENT_ACTOR_RESOLVER,
1653
2322
  AGENT_APPROVAL_PORT,
2323
+ AGENT_CONSOLE_ID,
2324
+ AGENT_DASHBOARD_CONFIG_ID,
1654
2325
  AGENT_GOVERNANCE_QUERIES,
1655
2326
  AGENT_PRICING_STORE,
2327
+ AVIARY_CONSOLES_SLOT,
1656
2328
  AgentApiController,
1657
2329
  AgentApiModule,
2330
+ AgentConsoleRegistration,
2331
+ AgentConsolesController,
1658
2332
  AgentDashboardModule,
2333
+ AgentDevToolsRegistration,
1659
2334
  AgentUiController,
1660
2335
  CONSOLE_AUTHORIZE_MISSING_WARNING,
1661
2336
  CONSOLE_GATE,
1662
2337
  CONSOLE_PAGE_METADATA,
2338
+ CONSOLE_PROBE_TIMEOUT_MS,
1663
2339
  ConsoleAccessDenied,
1664
2340
  ConsoleAuthorizeFilter,
1665
2341
  ConsoleAuthorizeGuard,
@@ -1668,13 +2344,18 @@ export {
1668
2344
  DASHBOARD_APPROVAL_ACTOR_REF,
1669
2345
  DASHBOARD_BASE_PATH,
1670
2346
  DASHBOARD_CONSOLE_OPTIONS,
2347
+ DASHBOARD_DEV_TOOLS,
1671
2348
  DashboardConsoleAccess,
1672
2349
  DashboardService,
1673
2350
  UseConsoleAuthorize,
1674
2351
  agentDashboardMountPaths,
2352
+ agentDevTools,
2353
+ anyAuthorize,
1675
2354
  authorizeByToken,
2355
+ listVisibleConsoles,
1676
2356
  normalizeConsoleResult,
1677
2357
  parsePriceInput,
2358
+ registerAviaryConsole,
1678
2359
  resolveConsoleAuthorize
1679
2360
  };
1680
2361
  //# sourceMappingURL=index.js.map