@dudousxd/nestjs-agent-dashboard 0.19.1 → 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.
@@ -1,7 +1,8 @@
1
1
  import { ActorSpendRow, AgentGovernanceQueries, ActorDirectory, AgentPricingStore, AgentApprovalPort, GovernanceRange, ModelSpendRow, UsageTrendPoint, FeatureSpendRow, ThreadSpendRow, RunMetrics, RunAgentBreakdownRow, RunErrorBreakdownRow, RunTrendPoint, RecentRunRow, ToolCallActivityRow, GovernancePageQuery, ToolCallWhere, GovernancePage, RunWhere, PendingApprovalRow, ApprovalWhere, GovernanceRunDetail, GovernanceThreadDetailQuery, GovernanceThreadDetail, ThreadActivityRow, ToolStatRow, GenuiStats, ThreadWhere, CurrentModelPrice, ActorResolver, ModelPriceInput } from '@dudousxd/nestjs-agent-core';
2
2
  export { ActorDirectory } from '@dudousxd/nestjs-agent-core';
3
3
  import { Observable } from 'rxjs';
4
- import { DynamicModule, StreamableFile } from '@nestjs/common';
4
+ import { ExecutionContext, Type, Logger, DynamicModule, StreamableFile, OnModuleInit, HttpException, ExceptionFilter, ArgumentsHost, CanActivate, OnModuleDestroy } from '@nestjs/common';
5
+ import { ModuleRef, HttpAdapterHost } from '@nestjs/core';
5
6
 
6
7
  /** An actor-scoped row decorated with its resolved display label — `null` when unbound or unresolved. */
7
8
  interface WithActorLabel {
@@ -151,8 +152,9 @@ declare class DashboardService {
151
152
  * JSON + SSE API consumed by the AI-gateway console SPA. Mounted at `apiBasePath` (set by
152
153
  * `RouterModule` in {@link AgentDashboardModule.forRoot}), so the controller routes are relative.
153
154
  *
154
- * Who reaches it is the app's: `decorators` on `AgentDashboardModule.forRoot`, or the app mounts it
155
- * itself (`routes: false`) — with `@nestjs/authentication` / `@nestjs/authorization`.
155
+ * Who reaches it: `authorize` on `AgentDashboardModule.forRoot` (checked by `ConsoleAuthorizeGuard`,
156
+ * after the app's `decorators`). Mounted by the app itself (`routes: false`), apply
157
+ * `UseConsoleAuthorize()` to your subclass.
156
158
  */
157
159
  declare class AgentApiController {
158
160
  private readonly dashboard;
@@ -293,6 +295,90 @@ interface AgentDashboardMountPathsOptions {
293
295
  */
294
296
  declare function agentDashboardMountPaths(options?: AgentDashboardMountPathsOptions): string[];
295
297
 
298
+ /**
299
+ * What an `authorize` check answers. An allow may name who it let in (`caller`, for example
300
+ * `'user:42'`): the dev MCP endpoint logs it as the caller of a developer tool. Never put a secret
301
+ * in it.
302
+ */
303
+ type ConsoleAuthorizeResult = boolean | {
304
+ allowed: true;
305
+ caller?: string;
306
+ } | {
307
+ allowed: false;
308
+ reason?: 'unauthenticated' | 'forbidden';
309
+ } | {
310
+ redirectTo: string;
311
+ };
312
+ /** Class form: an @Injectable() provider (constructor injection works). */
313
+ interface ConsoleAuthorizer {
314
+ authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;
315
+ }
316
+ type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;
317
+ /** @nestjs/authorization policy check: policy[ability](user). */
318
+ interface ConsolePolicyCheck {
319
+ policy: Type<unknown>;
320
+ ability: string;
321
+ }
322
+ /** Who reaches the console. */
323
+ type ConsoleAuthorize = ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>) | ConsoleAuthorizerClass | (() => Promise<{
324
+ default: ConsoleAuthorizerClass;
325
+ }>) | ConsolePolicyCheck | string;
326
+ /** A normalized `authorize` answer. */
327
+ type ConsoleDecision = {
328
+ allowed: true;
329
+ caller?: string;
330
+ } | {
331
+ allowed: false;
332
+ reason: 'unauthenticated' | 'forbidden';
333
+ redirectTo?: string;
334
+ };
335
+ /** What a resolved `authorize` is: called once per request, never throws. */
336
+ type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;
337
+ /**
338
+ * The provider that resolves gate names (`authorize: 'console.agent'`), bound globally by
339
+ * `@dudousxd/nestjs-authz`'s `AuthzModule`. A registry symbol, so it is found without depending on
340
+ * that package.
341
+ */
342
+ declare const CONSOLE_GATE: unique symbol;
343
+ interface ConsoleGate {
344
+ allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;
345
+ }
346
+ /** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */
347
+ declare const CONSOLE_AUTHORIZE_MISSING_WARNING = "[@dudousxd/nestjs-agent-dashboard] console has no `authorize` configured: it is open outside production and denies every request in production. Set `authorize` in AgentDashboardModule.forRoot().";
348
+ /** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */
349
+ declare function normalizeConsoleResult(result: unknown): ConsoleDecision;
350
+ /**
351
+ * Resolves `authorize` into one function the guard calls per request. Configuration mistakes (a
352
+ * gate name without nestjs-authz, a policy without `@nestjs/authorization`) throw here, so call it
353
+ * at boot. The returned function never throws: an error is a `forbidden`.
354
+ *
355
+ * `authorize` omitted: open outside production, `forbidden` for every request in production (read
356
+ * when this runs), and one warning logged (unless `warnIfMissing: false`).
357
+ */
358
+ declare function resolveConsoleAuthorize(authorize: ConsoleAuthorize | undefined, moduleRef: ModuleRef, options?: {
359
+ logger?: Logger;
360
+ warnIfMissing?: boolean;
361
+ }): Promise<ConsoleDecide>;
362
+ /**
363
+ * `authorize` by a bearer token: allows when `Authorization: Bearer <token>` matches (compared in
364
+ * constant time), else `unauthenticated`. Header only. An empty token throws here, at config time.
365
+ * The allow names its caller `token:<first 12 hex of sha256(token)>`, never the token.
366
+ */
367
+ declare function authorizeByToken(options: {
368
+ token: string | undefined;
369
+ }): (context: ExecutionContext) => ConsoleAuthorizeResult;
370
+ /**
371
+ * One `authorize` from several: allowed when any form allows (checked in order, stopping at the
372
+ * first that does, whose `caller` it keeps); otherwise `unauthenticated` when any form said so (keeping the first
373
+ * `redirectTo`), else `forbidden`. Each form is any `authorize` value: a function, a class, a lazy
374
+ * import, `{ policy, ability }` or a gate name.
375
+ *
376
+ * ```ts
377
+ * authorize: anyAuthorize(authorizeByToken({ token: process.env.CONSOLE_TOKEN }), AdminSessionAuthorizer)
378
+ * ```
379
+ */
380
+ declare function anyAuthorize(...forms: ConsoleAuthorize[]): ConsoleAuthorizerClass;
381
+
296
382
  /**
297
383
  * `TReq` mirrors core's `ActorResolver<TReq>` convention: the transport request type
298
384
  * {@link AgentDashboardOptions.approvalActorRef} receives. Defaults to `unknown`.
@@ -310,23 +396,28 @@ interface AgentDashboardOptions<TReq = unknown> {
310
396
  */
311
397
  apiBasePath?: string;
312
398
  /**
313
- * Who reaches the console: class decorators applied to every controller this module mounts — the
314
- * ecosystem's convention, like `@nestjs/mcp`'s `decorators` — with the official tools, e.g.
315
- * `[UseGuards(AuthenticationGuard), Can(ConsolePolicy, 'view')]` from `@nestjs/authentication` /
316
- * `@nestjs/authorization`, or a function as a guard instance, `UseGuards({ canActivate })`.
317
- * Applied to a per-registration subclass, so nothing sticks to the shared classes.
399
+ * Who reaches the console (the SPA and every API/SSE route): an inline function
400
+ * `(ctx) => boolean | { allowed } | { redirectTo }`, an `@Injectable()` `ConsoleAuthorizer` class,
401
+ * a lazy import of one, `{ policy, ability }` (`@nestjs/authorization`), or a gate name
402
+ * (`@dudousxd/nestjs-authz`). `authorizeByToken({ token })` checks a bearer token.
318
403
  *
319
- * In production (`NODE_ENV=production`) the console refuses to boot without them, unless the app
320
- * mounts the routes itself (`routes: false`): a governance console is never served open there by
321
- * default. Outside production the routes are open (and to `@nestjs/authorization`'s
322
- * deny-by-default guard: `CanAnyone()`). Static wiring.
404
+ * Omitted: open outside production; in production every request answers 403 (and a warning is
405
+ * logged at boot).
406
+ */
407
+ authorize?: ConsoleAuthorize;
408
+ /**
409
+ * Class decorators applied to every controller this module mounts, before the console's own
410
+ * `authorize` check — e.g. `[UseGuards(AuthenticationGuard)]` so `request.user` is set when
411
+ * `authorize` runs. Applied to a per-registration subclass, so nothing sticks to the shared
412
+ * classes. They do not replace `authorize`. Static wiring.
323
413
  */
324
414
  decorators?: ClassDecorator[];
325
415
  /**
326
416
  * `false` mounts none of the console's routes, while its services still run — the way NestJS's
327
417
  * own packages leave routes to you: serve them from controllers of your own, protected with the
328
418
  * official tools, by extending the exported `AgentApiController` / `AgentUiController` under your
329
- * own path and decorators (the module exports what they inject). Static wiring.
419
+ * own path and decorators (the module exports what they inject); `UseConsoleAuthorize()` applies
420
+ * this module's `authorize` to them. Static wiring.
330
421
  */
331
422
  routes?: boolean;
332
423
  /** Extra `imports` merged into the console's modules — what a decorator's guard injects. */
@@ -342,6 +433,12 @@ interface AgentDashboardOptions<TReq = unknown> {
342
433
  * `executedByRef` unset — the explicit override wins outright, with no resolver fallback.
343
434
  */
344
435
  approvalActorRef?: (req: TReq) => string | undefined;
436
+ /**
437
+ * Register the console's reads as developer tools on the agent MCP server's dev endpoint
438
+ * (`/mcp/aviary`), gated by this console's `authorize`. Default: on when the console's routes are
439
+ * mounted, off with `routes: false`.
440
+ */
441
+ devTools?: boolean;
345
442
  }
346
443
  /** Holds the JSON API + SSE controller and its read service, mounted on its own path. */
347
444
  declare class AgentApiModule {
@@ -350,12 +447,13 @@ declare class AgentApiModule {
350
447
  decorators?: ClassDecorator[];
351
448
  routes?: boolean;
352
449
  approvalActorRef?: (req: TReq) => string | undefined;
450
+ authorize?: ConsoleAuthorize;
451
+ devTools?: boolean;
353
452
  }): DynamicModule;
354
453
  }
355
454
  /**
356
455
  * The AI-gateway console: the SPA at `basePath` and its JSON API at `apiBasePath`. Who reaches them
357
- * is the app's — `decorators`, or the app mounts them itself (`routes: false`) — with
358
- * `@nestjs/authentication` / `@nestjs/authorization`.
456
+ * is `authorize` (see {@link AgentDashboardOptions.authorize}).
359
457
  */
360
458
  declare class AgentDashboardModule {
361
459
  static forRoot<TReq = unknown>(options?: AgentDashboardOptions<TReq>): DynamicModule;
@@ -380,8 +478,9 @@ interface UiPageResponse {
380
478
  * `<base>/assets`). The path comes from `RouterModule` (set by
381
479
  * {@link AgentDashboardModule.forRoot}({ basePath })), so the controller routes are relative.
382
480
  *
383
- * Who reaches it is the app's: `decorators` on `AgentDashboardModule.forRoot`, or the app mounts it
384
- * itself (`routes: false`) — with `@nestjs/authentication` / `@nestjs/authorization`.
481
+ * Who reaches it: `authorize` on `AgentDashboardModule.forRoot` (checked by `ConsoleAuthorizeGuard`,
482
+ * after the app's `decorators`). Mounted by the app itself (`routes: false`), apply
483
+ * `UseConsoleAuthorize()` to your subclass.
385
484
  */
386
485
  declare class AgentUiController {
387
486
  private readonly basePath;
@@ -392,6 +491,105 @@ declare class AgentUiController {
392
491
  asset(file: string): StreamableFile;
393
492
  }
394
493
 
494
+ /** What {@link DashboardConsoleAccess} is built from (bound per `forRoot`). */
495
+ interface DashboardConsoleOptions {
496
+ authorize?: ConsoleAuthorize;
497
+ }
498
+ /** Holds the console's resolved `authorize` check for one registration. */
499
+ declare class DashboardConsoleAccess implements OnModuleInit {
500
+ #private;
501
+ private readonly options;
502
+ private readonly moduleRef;
503
+ constructor(options: DashboardConsoleOptions, moduleRef: ModuleRef);
504
+ /** Resolves `authorize` at boot: a configuration mistake fails the boot, and the warning logs once. */
505
+ onModuleInit(): Promise<void>;
506
+ /** The decision for one request. */
507
+ check(context: ExecutionContext): Promise<ConsoleDecision>;
508
+ }
509
+ /** Marks a controller or handler as serving pages: a denied browser navigation gets HTML or a 302. */
510
+ declare const CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-agent-dashboard:console-page";
511
+ /**
512
+ * On a controller or a handler that serves pages: a denied `GET` that accepts HTML gets a page (or a
513
+ * 302 for `redirectTo`), not JSON.
514
+ */
515
+ declare const ConsolePage: () => ClassDecorator & MethodDecorator;
516
+ /** Thrown by {@link ConsoleAuthorizeGuard}; its body is the JSON answer even without the filter. */
517
+ declare class ConsoleAccessDenied extends HttpException {
518
+ readonly decision: Exclude<ConsoleDecision, {
519
+ allowed: true;
520
+ }>;
521
+ readonly page: boolean;
522
+ constructor(decision: Exclude<ConsoleDecision, {
523
+ allowed: true;
524
+ }>, page: boolean);
525
+ }
526
+ /**
527
+ * Runs the console's `authorize` on every route of a controller it guards. Applied by the module
528
+ * after the app's `decorators` (so authentication guards have already set `request.user`); a
529
+ * self-mounted controller (`routes: false`) applies it with {@link UseConsoleAuthorize}.
530
+ */
531
+ declare class ConsoleAuthorizeGuard implements CanActivate {
532
+ private readonly access;
533
+ constructor(access: DashboardConsoleAccess);
534
+ canActivate(context: ExecutionContext): Promise<boolean>;
535
+ }
536
+ /** Writes a console denial: JSON for API calls; a 302 or an HTML page (403 / 401) for page navigations. */
537
+ declare class ConsoleAuthorizeFilter implements ExceptionFilter<ConsoleAccessDenied> {
538
+ private readonly adapterHost;
539
+ constructor(adapterHost: HttpAdapterHost);
540
+ catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void;
541
+ }
542
+ /**
543
+ * The console's access check on a controller of your own (with `routes: false`): the
544
+ * {@link ConsoleAuthorizeGuard} plus the filter that writes its denials. Write it ABOVE your own
545
+ * authentication decorators: class decorators apply bottom-up, so it then runs after them and
546
+ * `request.user` is set.
547
+ */
548
+ declare function UseConsoleAuthorize(): ClassDecorator & MethodDecorator;
549
+
550
+ interface DevToolInputProperty {
551
+ type: 'string' | 'number' | 'integer' | 'boolean' | 'array';
552
+ description?: string;
553
+ enum?: readonly string[];
554
+ items?: {
555
+ type: 'string';
556
+ };
557
+ minimum?: number;
558
+ maximum?: number;
559
+ }
560
+ interface DevToolInputSchema {
561
+ type: 'object';
562
+ properties: Record<string, DevToolInputProperty>;
563
+ required?: string[];
564
+ }
565
+ /** One developer tool, as the dev-tools slot takes it. */
566
+ interface AgentDevTool {
567
+ name: string;
568
+ kind: 'read' | 'action';
569
+ description: string;
570
+ input: DevToolInputSchema;
571
+ run(input: Record<string, unknown>): Promise<unknown>;
572
+ }
573
+ /**
574
+ * The agent's developer tools (lib `agent`): the governance console's reads, through the same
575
+ * {@link DashboardService} methods its API serves. No actions: approving a tool call over MCP would
576
+ * bypass the human approval it exists for.
577
+ */
578
+ declare function agentDevTools(dashboard: DashboardService): AgentDevTool[];
579
+ /**
580
+ * Registers the agent's tools into the dev-tools slot, gated by the console's own resolved
581
+ * `authorize`, when the console is mounted (or `devTools: true`). Removed again on shutdown.
582
+ */
583
+ declare class AgentDevToolsRegistration implements OnModuleInit, OnModuleDestroy {
584
+ #private;
585
+ private readonly enabled;
586
+ private readonly dashboard;
587
+ private readonly access;
588
+ constructor(enabled: boolean, dashboard: DashboardService, access: DashboardConsoleAccess);
589
+ onModuleInit(): void;
590
+ onModuleDestroy(): void;
591
+ }
592
+
395
593
  /**
396
594
  * Minimal shape guard for a `POST <api>/pricing` body — rejects junk before it reaches
397
595
  * `AgentPricingStore.upsertModelPrice`. Not a full schema validator (the store adapter owns real
@@ -455,5 +653,9 @@ declare const DASHBOARD_API_PATH: unique symbol;
455
653
  * injecting it needs no `@Optional()`.
456
654
  */
457
655
  declare const DASHBOARD_APPROVAL_ACTOR_REF: unique symbol;
656
+ /** DI token carrying the console's `authorize` (and whether to warn when it is omitted). */
657
+ declare const DASHBOARD_CONSOLE_OPTIONS: unique symbol;
658
+ /** DI token: whether the agent's dev tools register into the dev-tools slot. */
659
+ declare const DASHBOARD_DEV_TOOLS: unique symbol;
458
660
 
459
- export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, AgentUiController, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_BASE_PATH, DashboardService, type LiveAgentEvent, type ReliabilityOverview, type RunDetailWithLabel, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadDetailWithLabel, type ThreadSpendRowWithLabel, type WithActorLabel, agentDashboardMountPaths, parsePriceInput };
661
+ export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, type AgentDevTool, AgentDevToolsRegistration, AgentUiController, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_BASE_PATH, DASHBOARD_CONSOLE_OPTIONS, DASHBOARD_DEV_TOOLS, DashboardConsoleAccess, DashboardService, type LiveAgentEvent, type ReliabilityOverview, type RunDetailWithLabel, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadDetailWithLabel, type ThreadSpendRowWithLabel, UseConsoleAuthorize, type WithActorLabel, agentDashboardMountPaths, agentDevTools, anyAuthorize, authorizeByToken, normalizeConsoleResult, parsePriceInput, resolveConsoleAuthorize };
@@ -1,7 +1,8 @@
1
1
  import { ActorSpendRow, AgentGovernanceQueries, ActorDirectory, AgentPricingStore, AgentApprovalPort, GovernanceRange, ModelSpendRow, UsageTrendPoint, FeatureSpendRow, ThreadSpendRow, RunMetrics, RunAgentBreakdownRow, RunErrorBreakdownRow, RunTrendPoint, RecentRunRow, ToolCallActivityRow, GovernancePageQuery, ToolCallWhere, GovernancePage, RunWhere, PendingApprovalRow, ApprovalWhere, GovernanceRunDetail, GovernanceThreadDetailQuery, GovernanceThreadDetail, ThreadActivityRow, ToolStatRow, GenuiStats, ThreadWhere, CurrentModelPrice, ActorResolver, ModelPriceInput } from '@dudousxd/nestjs-agent-core';
2
2
  export { ActorDirectory } from '@dudousxd/nestjs-agent-core';
3
3
  import { Observable } from 'rxjs';
4
- import { DynamicModule, StreamableFile } from '@nestjs/common';
4
+ import { ExecutionContext, Type, Logger, DynamicModule, StreamableFile, OnModuleInit, HttpException, ExceptionFilter, ArgumentsHost, CanActivate, OnModuleDestroy } from '@nestjs/common';
5
+ import { ModuleRef, HttpAdapterHost } from '@nestjs/core';
5
6
 
6
7
  /** An actor-scoped row decorated with its resolved display label — `null` when unbound or unresolved. */
7
8
  interface WithActorLabel {
@@ -151,8 +152,9 @@ declare class DashboardService {
151
152
  * JSON + SSE API consumed by the AI-gateway console SPA. Mounted at `apiBasePath` (set by
152
153
  * `RouterModule` in {@link AgentDashboardModule.forRoot}), so the controller routes are relative.
153
154
  *
154
- * Who reaches it is the app's: `decorators` on `AgentDashboardModule.forRoot`, or the app mounts it
155
- * itself (`routes: false`) — with `@nestjs/authentication` / `@nestjs/authorization`.
155
+ * Who reaches it: `authorize` on `AgentDashboardModule.forRoot` (checked by `ConsoleAuthorizeGuard`,
156
+ * after the app's `decorators`). Mounted by the app itself (`routes: false`), apply
157
+ * `UseConsoleAuthorize()` to your subclass.
156
158
  */
157
159
  declare class AgentApiController {
158
160
  private readonly dashboard;
@@ -293,6 +295,90 @@ interface AgentDashboardMountPathsOptions {
293
295
  */
294
296
  declare function agentDashboardMountPaths(options?: AgentDashboardMountPathsOptions): string[];
295
297
 
298
+ /**
299
+ * What an `authorize` check answers. An allow may name who it let in (`caller`, for example
300
+ * `'user:42'`): the dev MCP endpoint logs it as the caller of a developer tool. Never put a secret
301
+ * in it.
302
+ */
303
+ type ConsoleAuthorizeResult = boolean | {
304
+ allowed: true;
305
+ caller?: string;
306
+ } | {
307
+ allowed: false;
308
+ reason?: 'unauthenticated' | 'forbidden';
309
+ } | {
310
+ redirectTo: string;
311
+ };
312
+ /** Class form: an @Injectable() provider (constructor injection works). */
313
+ interface ConsoleAuthorizer {
314
+ authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;
315
+ }
316
+ type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;
317
+ /** @nestjs/authorization policy check: policy[ability](user). */
318
+ interface ConsolePolicyCheck {
319
+ policy: Type<unknown>;
320
+ ability: string;
321
+ }
322
+ /** Who reaches the console. */
323
+ type ConsoleAuthorize = ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>) | ConsoleAuthorizerClass | (() => Promise<{
324
+ default: ConsoleAuthorizerClass;
325
+ }>) | ConsolePolicyCheck | string;
326
+ /** A normalized `authorize` answer. */
327
+ type ConsoleDecision = {
328
+ allowed: true;
329
+ caller?: string;
330
+ } | {
331
+ allowed: false;
332
+ reason: 'unauthenticated' | 'forbidden';
333
+ redirectTo?: string;
334
+ };
335
+ /** What a resolved `authorize` is: called once per request, never throws. */
336
+ type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;
337
+ /**
338
+ * The provider that resolves gate names (`authorize: 'console.agent'`), bound globally by
339
+ * `@dudousxd/nestjs-authz`'s `AuthzModule`. A registry symbol, so it is found without depending on
340
+ * that package.
341
+ */
342
+ declare const CONSOLE_GATE: unique symbol;
343
+ interface ConsoleGate {
344
+ allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;
345
+ }
346
+ /** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */
347
+ declare const CONSOLE_AUTHORIZE_MISSING_WARNING = "[@dudousxd/nestjs-agent-dashboard] console has no `authorize` configured: it is open outside production and denies every request in production. Set `authorize` in AgentDashboardModule.forRoot().";
348
+ /** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */
349
+ declare function normalizeConsoleResult(result: unknown): ConsoleDecision;
350
+ /**
351
+ * Resolves `authorize` into one function the guard calls per request. Configuration mistakes (a
352
+ * gate name without nestjs-authz, a policy without `@nestjs/authorization`) throw here, so call it
353
+ * at boot. The returned function never throws: an error is a `forbidden`.
354
+ *
355
+ * `authorize` omitted: open outside production, `forbidden` for every request in production (read
356
+ * when this runs), and one warning logged (unless `warnIfMissing: false`).
357
+ */
358
+ declare function resolveConsoleAuthorize(authorize: ConsoleAuthorize | undefined, moduleRef: ModuleRef, options?: {
359
+ logger?: Logger;
360
+ warnIfMissing?: boolean;
361
+ }): Promise<ConsoleDecide>;
362
+ /**
363
+ * `authorize` by a bearer token: allows when `Authorization: Bearer <token>` matches (compared in
364
+ * constant time), else `unauthenticated`. Header only. An empty token throws here, at config time.
365
+ * The allow names its caller `token:<first 12 hex of sha256(token)>`, never the token.
366
+ */
367
+ declare function authorizeByToken(options: {
368
+ token: string | undefined;
369
+ }): (context: ExecutionContext) => ConsoleAuthorizeResult;
370
+ /**
371
+ * One `authorize` from several: allowed when any form allows (checked in order, stopping at the
372
+ * first that does, whose `caller` it keeps); otherwise `unauthenticated` when any form said so (keeping the first
373
+ * `redirectTo`), else `forbidden`. Each form is any `authorize` value: a function, a class, a lazy
374
+ * import, `{ policy, ability }` or a gate name.
375
+ *
376
+ * ```ts
377
+ * authorize: anyAuthorize(authorizeByToken({ token: process.env.CONSOLE_TOKEN }), AdminSessionAuthorizer)
378
+ * ```
379
+ */
380
+ declare function anyAuthorize(...forms: ConsoleAuthorize[]): ConsoleAuthorizerClass;
381
+
296
382
  /**
297
383
  * `TReq` mirrors core's `ActorResolver<TReq>` convention: the transport request type
298
384
  * {@link AgentDashboardOptions.approvalActorRef} receives. Defaults to `unknown`.
@@ -310,23 +396,28 @@ interface AgentDashboardOptions<TReq = unknown> {
310
396
  */
311
397
  apiBasePath?: string;
312
398
  /**
313
- * Who reaches the console: class decorators applied to every controller this module mounts — the
314
- * ecosystem's convention, like `@nestjs/mcp`'s `decorators` — with the official tools, e.g.
315
- * `[UseGuards(AuthenticationGuard), Can(ConsolePolicy, 'view')]` from `@nestjs/authentication` /
316
- * `@nestjs/authorization`, or a function as a guard instance, `UseGuards({ canActivate })`.
317
- * Applied to a per-registration subclass, so nothing sticks to the shared classes.
399
+ * Who reaches the console (the SPA and every API/SSE route): an inline function
400
+ * `(ctx) => boolean | { allowed } | { redirectTo }`, an `@Injectable()` `ConsoleAuthorizer` class,
401
+ * a lazy import of one, `{ policy, ability }` (`@nestjs/authorization`), or a gate name
402
+ * (`@dudousxd/nestjs-authz`). `authorizeByToken({ token })` checks a bearer token.
318
403
  *
319
- * In production (`NODE_ENV=production`) the console refuses to boot without them, unless the app
320
- * mounts the routes itself (`routes: false`): a governance console is never served open there by
321
- * default. Outside production the routes are open (and to `@nestjs/authorization`'s
322
- * deny-by-default guard: `CanAnyone()`). Static wiring.
404
+ * Omitted: open outside production; in production every request answers 403 (and a warning is
405
+ * logged at boot).
406
+ */
407
+ authorize?: ConsoleAuthorize;
408
+ /**
409
+ * Class decorators applied to every controller this module mounts, before the console's own
410
+ * `authorize` check — e.g. `[UseGuards(AuthenticationGuard)]` so `request.user` is set when
411
+ * `authorize` runs. Applied to a per-registration subclass, so nothing sticks to the shared
412
+ * classes. They do not replace `authorize`. Static wiring.
323
413
  */
324
414
  decorators?: ClassDecorator[];
325
415
  /**
326
416
  * `false` mounts none of the console's routes, while its services still run — the way NestJS's
327
417
  * own packages leave routes to you: serve them from controllers of your own, protected with the
328
418
  * official tools, by extending the exported `AgentApiController` / `AgentUiController` under your
329
- * own path and decorators (the module exports what they inject). Static wiring.
419
+ * own path and decorators (the module exports what they inject); `UseConsoleAuthorize()` applies
420
+ * this module's `authorize` to them. Static wiring.
330
421
  */
331
422
  routes?: boolean;
332
423
  /** Extra `imports` merged into the console's modules — what a decorator's guard injects. */
@@ -342,6 +433,12 @@ interface AgentDashboardOptions<TReq = unknown> {
342
433
  * `executedByRef` unset — the explicit override wins outright, with no resolver fallback.
343
434
  */
344
435
  approvalActorRef?: (req: TReq) => string | undefined;
436
+ /**
437
+ * Register the console's reads as developer tools on the agent MCP server's dev endpoint
438
+ * (`/mcp/aviary`), gated by this console's `authorize`. Default: on when the console's routes are
439
+ * mounted, off with `routes: false`.
440
+ */
441
+ devTools?: boolean;
345
442
  }
346
443
  /** Holds the JSON API + SSE controller and its read service, mounted on its own path. */
347
444
  declare class AgentApiModule {
@@ -350,12 +447,13 @@ declare class AgentApiModule {
350
447
  decorators?: ClassDecorator[];
351
448
  routes?: boolean;
352
449
  approvalActorRef?: (req: TReq) => string | undefined;
450
+ authorize?: ConsoleAuthorize;
451
+ devTools?: boolean;
353
452
  }): DynamicModule;
354
453
  }
355
454
  /**
356
455
  * The AI-gateway console: the SPA at `basePath` and its JSON API at `apiBasePath`. Who reaches them
357
- * is the app's — `decorators`, or the app mounts them itself (`routes: false`) — with
358
- * `@nestjs/authentication` / `@nestjs/authorization`.
456
+ * is `authorize` (see {@link AgentDashboardOptions.authorize}).
359
457
  */
360
458
  declare class AgentDashboardModule {
361
459
  static forRoot<TReq = unknown>(options?: AgentDashboardOptions<TReq>): DynamicModule;
@@ -380,8 +478,9 @@ interface UiPageResponse {
380
478
  * `<base>/assets`). The path comes from `RouterModule` (set by
381
479
  * {@link AgentDashboardModule.forRoot}({ basePath })), so the controller routes are relative.
382
480
  *
383
- * Who reaches it is the app's: `decorators` on `AgentDashboardModule.forRoot`, or the app mounts it
384
- * itself (`routes: false`) — with `@nestjs/authentication` / `@nestjs/authorization`.
481
+ * Who reaches it: `authorize` on `AgentDashboardModule.forRoot` (checked by `ConsoleAuthorizeGuard`,
482
+ * after the app's `decorators`). Mounted by the app itself (`routes: false`), apply
483
+ * `UseConsoleAuthorize()` to your subclass.
385
484
  */
386
485
  declare class AgentUiController {
387
486
  private readonly basePath;
@@ -392,6 +491,105 @@ declare class AgentUiController {
392
491
  asset(file: string): StreamableFile;
393
492
  }
394
493
 
494
+ /** What {@link DashboardConsoleAccess} is built from (bound per `forRoot`). */
495
+ interface DashboardConsoleOptions {
496
+ authorize?: ConsoleAuthorize;
497
+ }
498
+ /** Holds the console's resolved `authorize` check for one registration. */
499
+ declare class DashboardConsoleAccess implements OnModuleInit {
500
+ #private;
501
+ private readonly options;
502
+ private readonly moduleRef;
503
+ constructor(options: DashboardConsoleOptions, moduleRef: ModuleRef);
504
+ /** Resolves `authorize` at boot: a configuration mistake fails the boot, and the warning logs once. */
505
+ onModuleInit(): Promise<void>;
506
+ /** The decision for one request. */
507
+ check(context: ExecutionContext): Promise<ConsoleDecision>;
508
+ }
509
+ /** Marks a controller or handler as serving pages: a denied browser navigation gets HTML or a 302. */
510
+ declare const CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-agent-dashboard:console-page";
511
+ /**
512
+ * On a controller or a handler that serves pages: a denied `GET` that accepts HTML gets a page (or a
513
+ * 302 for `redirectTo`), not JSON.
514
+ */
515
+ declare const ConsolePage: () => ClassDecorator & MethodDecorator;
516
+ /** Thrown by {@link ConsoleAuthorizeGuard}; its body is the JSON answer even without the filter. */
517
+ declare class ConsoleAccessDenied extends HttpException {
518
+ readonly decision: Exclude<ConsoleDecision, {
519
+ allowed: true;
520
+ }>;
521
+ readonly page: boolean;
522
+ constructor(decision: Exclude<ConsoleDecision, {
523
+ allowed: true;
524
+ }>, page: boolean);
525
+ }
526
+ /**
527
+ * Runs the console's `authorize` on every route of a controller it guards. Applied by the module
528
+ * after the app's `decorators` (so authentication guards have already set `request.user`); a
529
+ * self-mounted controller (`routes: false`) applies it with {@link UseConsoleAuthorize}.
530
+ */
531
+ declare class ConsoleAuthorizeGuard implements CanActivate {
532
+ private readonly access;
533
+ constructor(access: DashboardConsoleAccess);
534
+ canActivate(context: ExecutionContext): Promise<boolean>;
535
+ }
536
+ /** Writes a console denial: JSON for API calls; a 302 or an HTML page (403 / 401) for page navigations. */
537
+ declare class ConsoleAuthorizeFilter implements ExceptionFilter<ConsoleAccessDenied> {
538
+ private readonly adapterHost;
539
+ constructor(adapterHost: HttpAdapterHost);
540
+ catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void;
541
+ }
542
+ /**
543
+ * The console's access check on a controller of your own (with `routes: false`): the
544
+ * {@link ConsoleAuthorizeGuard} plus the filter that writes its denials. Write it ABOVE your own
545
+ * authentication decorators: class decorators apply bottom-up, so it then runs after them and
546
+ * `request.user` is set.
547
+ */
548
+ declare function UseConsoleAuthorize(): ClassDecorator & MethodDecorator;
549
+
550
+ interface DevToolInputProperty {
551
+ type: 'string' | 'number' | 'integer' | 'boolean' | 'array';
552
+ description?: string;
553
+ enum?: readonly string[];
554
+ items?: {
555
+ type: 'string';
556
+ };
557
+ minimum?: number;
558
+ maximum?: number;
559
+ }
560
+ interface DevToolInputSchema {
561
+ type: 'object';
562
+ properties: Record<string, DevToolInputProperty>;
563
+ required?: string[];
564
+ }
565
+ /** One developer tool, as the dev-tools slot takes it. */
566
+ interface AgentDevTool {
567
+ name: string;
568
+ kind: 'read' | 'action';
569
+ description: string;
570
+ input: DevToolInputSchema;
571
+ run(input: Record<string, unknown>): Promise<unknown>;
572
+ }
573
+ /**
574
+ * The agent's developer tools (lib `agent`): the governance console's reads, through the same
575
+ * {@link DashboardService} methods its API serves. No actions: approving a tool call over MCP would
576
+ * bypass the human approval it exists for.
577
+ */
578
+ declare function agentDevTools(dashboard: DashboardService): AgentDevTool[];
579
+ /**
580
+ * Registers the agent's tools into the dev-tools slot, gated by the console's own resolved
581
+ * `authorize`, when the console is mounted (or `devTools: true`). Removed again on shutdown.
582
+ */
583
+ declare class AgentDevToolsRegistration implements OnModuleInit, OnModuleDestroy {
584
+ #private;
585
+ private readonly enabled;
586
+ private readonly dashboard;
587
+ private readonly access;
588
+ constructor(enabled: boolean, dashboard: DashboardService, access: DashboardConsoleAccess);
589
+ onModuleInit(): void;
590
+ onModuleDestroy(): void;
591
+ }
592
+
395
593
  /**
396
594
  * Minimal shape guard for a `POST <api>/pricing` body — rejects junk before it reaches
397
595
  * `AgentPricingStore.upsertModelPrice`. Not a full schema validator (the store adapter owns real
@@ -455,5 +653,9 @@ declare const DASHBOARD_API_PATH: unique symbol;
455
653
  * injecting it needs no `@Optional()`.
456
654
  */
457
655
  declare const DASHBOARD_APPROVAL_ACTOR_REF: unique symbol;
656
+ /** DI token carrying the console's `authorize` (and whether to warn when it is omitted). */
657
+ declare const DASHBOARD_CONSOLE_OPTIONS: unique symbol;
658
+ /** DI token: whether the agent's dev tools register into the dev-tools slot. */
659
+ declare const DASHBOARD_DEV_TOOLS: unique symbol;
458
660
 
459
- export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, AgentUiController, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_BASE_PATH, DashboardService, type LiveAgentEvent, type ReliabilityOverview, type RunDetailWithLabel, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadDetailWithLabel, type ThreadSpendRowWithLabel, type WithActorLabel, agentDashboardMountPaths, parsePriceInput };
661
+ export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, type AgentDevTool, AgentDevToolsRegistration, AgentUiController, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_BASE_PATH, DASHBOARD_CONSOLE_OPTIONS, DASHBOARD_DEV_TOOLS, DashboardConsoleAccess, DashboardService, type LiveAgentEvent, type ReliabilityOverview, type RunDetailWithLabel, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadDetailWithLabel, type ThreadSpendRowWithLabel, UseConsoleAuthorize, type WithActorLabel, agentDashboardMountPaths, agentDevTools, anyAuthorize, authorizeByToken, normalizeConsoleResult, parsePriceInput, resolveConsoleAuthorize };