@dudousxd/nestjs-agent-dashboard 0.21.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.
- package/dist/server/index.cjs +276 -60
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +92 -1
- package/dist/server/index.d.ts +92 -1
- package/dist/server/index.js +255 -47
- package/dist/server/index.js.map +1 -1
- package/dist/spa/assets/index-BW1bqmTf.css +1 -0
- package/dist/spa/assets/index-C4U9WEBt.js +1 -0
- package/dist/spa/assets/{preview-CbqJ2CAQ.js → preview-CJ1UfhOA.js} +1 -1
- package/dist/spa/index.html +3 -3
- package/dist/spa/preview.html +3 -3
- package/package.json +1 -1
- package/dist/spa/assets/index-ClnZsvJ1.css +0 -1
- package/dist/spa/assets/index-D3yTBCU0.js +0 -1
- /package/dist/spa/assets/{index-DS11tOEH.js → index-Br3YfiYJ.js} +0 -0
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/agent-api.controller.ts","../../src/server/dashboard.service.ts","../../src/server/parse-approval-decision.ts","../../src/server/parse-price-input.ts","../../src/server/tokens.ts","../../src/server/parse-page-query.ts","../../src/server/normalize-path.ts","../../src/server/agent-dashboard-mount-paths.ts","../../src/server/agent-dashboard.module.ts","../../src/server/agent-ui.controller.ts","../../src/server/console-authorize.guard.ts","../../src/server/console-authorize.ts","../../src/server/dev-tools.ts"],"sourcesContent":["import { CanAnyone } from '@dudousxd/nestjs-agent-core';\nimport type {\n ActorResolver,\n CurrentModelPrice,\n GenuiStats,\n GovernancePage,\n PendingApprovalRow,\n RecentRunRow,\n ToolCallActivityRow,\n ToolStatRow,\n} from '@dudousxd/nestjs-agent-core';\nimport {\n Body,\n Controller,\n Get,\n HttpCode,\n Inject,\n Optional,\n Param,\n Post,\n Query,\n Req,\n Sse,\n} from '@nestjs/common';\nimport type { Observable } from 'rxjs';\nimport {\n DashboardService,\n type LiveAgentEvent,\n type ReliabilityOverview,\n type RunDetailWithLabel,\n type SpendOverview,\n type ThreadActivityRowWithLabel,\n type ThreadDetailWithLabel,\n type ThreadSpendRowWithLabel,\n} from './dashboard.service.js';\nimport {\n parseApprovalWhere,\n parsePageNumber,\n parseRunWhere,\n parseThreadWhere,\n parseToolCallWhere,\n} from './parse-page-query.js';\nimport { AGENT_ACTOR_RESOLVER, DASHBOARD_APPROVAL_ACTOR_REF } from './tokens.js';\n\nconst DAY_MS = 86_400_000;\nconst ISO_DAY = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** A `YYYY-MM-DD` UTC day string `daysAgo` days before now (0 = today). */\nfunction utcDay(daysAgo: number): string {\n return new Date(Date.now() - daysAgo * DAY_MS).toISOString().slice(0, 10);\n}\n\n/** Accept a client-supplied `YYYY-MM-DD`, or fall back to `fallback`; guards against junk input. */\nfunction dayOr(value: string | undefined, fallback: string): string {\n return value !== undefined && ISO_DAY.test(value) ? value : fallback;\n}\n\n/** Resolve the `from`/`to` query params into a validated range, defaulting to the last 30 days. */\nfunction resolveRange(\n from: string | undefined,\n to: string | undefined,\n): {\n fromDay: string;\n toDay: string;\n} {\n return { fromDay: dayOr(from, utcDay(29)), toDay: dayOr(to, utcDay(0)) };\n}\n\n/** Parse a `limit` query param, clamped to a sane window; falls back to `fallback` when absent/junk. */\nfunction parseLimit(value: string | undefined, fallback: number): number {\n const parsed = value === undefined ? Number.NaN : Number.parseInt(value, 10);\n if (!Number.isFinite(parsed)) return fallback;\n return Math.max(1, Math.min(200, parsed));\n}\n\n/**\n * JSON + SSE API consumed by the AI-gateway console SPA. Mounted at `apiBasePath` (set by\n * `RouterModule` in {@link AgentDashboardModule.forRoot}), so the controller routes are relative.\n *\n * Who reaches it: `authorize` on `AgentDashboardModule.forRoot` (checked by `ConsoleAuthorizeGuard`,\n * after the app's `decorators`). Mounted by the app itself (`routes: false`), apply\n * `UseConsoleAuthorize()` to your subclass.\n */\n@CanAnyone()\n@Controller()\nexport class AgentApiController {\n constructor(\n private readonly dashboard: DashboardService,\n // `useValue` even when the host omitted `approvalActorRef` — no `@Optional()` needed (the token\n // is always bound; see DASHBOARD_APPROVAL_ACTOR_REF's TSDoc).\n @Inject(DASHBOARD_APPROVAL_ACTOR_REF)\n private readonly approvalActorRef: ((req: unknown) => string | undefined) | undefined,\n // The app's identity seam, bound + exported by the (global) AgentModule — the DEFAULT decider\n // attribution when no `approvalActorRef` override is configured. Genuinely `@Optional()`: the\n // dashboard can be mounted without AgentModule (read-model only), where nothing binds it.\n @Optional()\n @Inject(AGENT_ACTOR_RESOLVER)\n private readonly actorResolver?: ActorResolver,\n ) {}\n\n /** `{ byModel, byActor, trend }` for a day range (defaults to the last 30 days). */\n @Get('spend')\n spend(@Query('from') from?: string, @Query('to') to?: string): Promise<SpendOverview> {\n return this.dashboard.spend(resolveRange(from, to));\n }\n\n /** Top threads by cost (default 10, max 200) for a day range (defaults to the last 30 days). */\n @Get('top-threads')\n topThreads(\n @Query('from') from?: string,\n @Query('to') to?: string,\n @Query('limit') limit?: string,\n ): Promise<ThreadSpendRowWithLabel[]> {\n return this.dashboard.topThreads(resolveRange(from, to), parseLimit(limit, 10));\n }\n\n /** `{ metrics, byAgent, errors, trend }` for a day range (defaults to the last 30 days). */\n @Get('reliability')\n reliability(\n @Query('from') from?: string,\n @Query('to') to?: string,\n ): Promise<ReliabilityOverview> {\n return this.dashboard.reliability(resolveRange(from, to));\n }\n\n /** Most recent runs (default 50, max 200) for the Reliability recent-runs table. */\n @Get('runs')\n runs(@Query('limit') limit?: string): Promise<RecentRunRow[]> {\n return this.dashboard.recentRuns(parseLimit(limit, 50));\n }\n\n /**\n * Paged, filterable runs for the Reliability recent-runs table. `page` (default 1), `limit`\n * (default 25, max 200), and `where[agentName]`/`where[status]`/`where[errorCode]`/\n * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.\n */\n @Get('runs-page')\n runsPage(\n @Query('page') page?: string,\n @Query('limit') limit?: string,\n @Query('where') where?: Record<string, string>,\n ): Promise<GovernancePage<RecentRunRow>> {\n const parsedWhere = parseRunWhere(where);\n return this.dashboard.runsPage({\n page: parsePageNumber(page),\n pageSize: parseLimit(limit, 25),\n ...(parsedWhere !== undefined ? { where: parsedWhere } : {}),\n });\n }\n\n /**\n * One run drill-down: the run, its owning thread's headline (actor-labeled) and its tool calls, in\n * ONE round trip. 404s on an unknown run id.\n *\n * Declared AFTER `runs-page` so the literal route wins over this parameterised one regardless of\n * how the router orders same-prefix paths.\n */\n @Get('runs/:runId')\n runDetail(@Param('runId') runId: string): Promise<RunDetailWithLabel> {\n return this.dashboard.runDetail(runId);\n }\n\n /** Most recent tool calls (default 50, max 200) for the activity feed. */\n @Get('tool-calls')\n toolCalls(@Query('limit') limit?: string): Promise<ToolCallActivityRow[]> {\n return this.dashboard.recentToolCalls(parseLimit(limit, 50));\n }\n\n /**\n * Paged, filterable tool calls for the Runs & tools activity table. `page` (default 1), `limit`\n * (default 25, max 200), and `where[toolName]`/`where[toolType]`/`where[status]`/`where[threadId]`/\n * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.\n */\n @Get('tool-calls-page')\n toolCallsPage(\n @Query('page') page?: string,\n @Query('limit') limit?: string,\n @Query('where') where?: Record<string, string>,\n ): Promise<GovernancePage<ToolCallActivityRow>> {\n const parsedWhere = parseToolCallWhere(where);\n return this.dashboard.toolCallsPage({\n page: parsePageNumber(page),\n pageSize: parseLimit(limit, 25),\n ...(parsedWhere !== undefined ? { where: parsedWhere } : {}),\n });\n }\n\n /**\n * Tool calls sitting `pending_approval` (default 50, max 200), oldest first — the approvals inbox.\n *\n * Capped with NO total, so a backlog past `limit` is invisible from the response alone. Kept\n * because the SPA still calls it; anything new should call `approvals-page` instead.\n */\n @Get('approvals')\n approvals(@Query('limit') limit?: string): Promise<PendingApprovalRow[]> {\n return this.dashboard.pendingApprovals(parseLimit(limit, 50));\n }\n\n /**\n * Paged, filterable approvals inbox, oldest first. `page` (default 1), `limit` (default 25, max\n * 200), and `where[toolName]`/`where[threadId]`/`where[actorRef]`/`where[agentName]`/\n * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.\n *\n * Unlike `GET approvals` this reports `total`, so the console can say \"showing N of M\" instead of\n * silently hiding pending decisions behind a row cap.\n */\n @Get('approvals-page')\n approvalsPage(\n @Query('page') page?: string,\n @Query('limit') limit?: string,\n @Query('where') where?: Record<string, string>,\n ): Promise<GovernancePage<PendingApprovalRow>> {\n const parsedWhere = parseApprovalWhere(where);\n return this.dashboard.approvalsPage({\n page: parsePageNumber(page),\n pageSize: parseLimit(limit, 25),\n ...(parsedWhere !== undefined ? { where: parsedWhere } : {}),\n });\n }\n\n /**\n * Decide a pending HITL tool call. Body `{ approved: boolean; reason?: string }`. 501s (via\n * `DashboardService.decideApproval`) when no `AGENT_APPROVAL_PORT` is bound. `executedByRef`\n * comes from {@link deciderRef} run against the live request.\n */\n @Post('approvals/:toolCallId')\n @HttpCode(204)\n async decideApproval(\n @Param('toolCallId') toolCallId: string,\n @Body() body: unknown,\n @Req() req: unknown,\n ): Promise<void> {\n await this.dashboard.decideApproval(toolCallId, body, await this.deciderRef(req));\n }\n\n /**\n * WHO decided, as an opaque ref: an explicit `approvalActorRef` override wins outright (no\n * resolver fallback, even when it returns `undefined`); otherwise the AgentModule-configured\n * actor resolver — the same identity seam chat requests use. A resolver that throws is an\n * unauthenticated/unreadable request, not an error to surface: the decision itself was already\n * authorized by the dashboard's guards, so the ref is simply omitted.\n */\n private async deciderRef(req: unknown): Promise<string | undefined> {\n if (this.approvalActorRef !== undefined) {\n return this.approvalActorRef(req);\n }\n if (this.actorResolver === undefined) {\n return undefined;\n }\n try {\n return (await this.actorResolver.resolve(req)).id;\n } catch {\n return undefined;\n }\n }\n\n /** Per-tool call/failure/rejection/latency rollup for a day range (defaults to the last 30 days). */\n @Get('tools')\n tools(@Query('from') from?: string, @Query('to') to?: string): Promise<ToolStatRow[]> {\n return this.dashboard.toolStats(resolveRange(from, to));\n }\n\n /**\n * Generative-UI telemetry for a day range (defaults to the last 30 days): `{ totals, byToolModel,\n * errors, byDay, costPerTurn, latency }`. 501s (via `DashboardService.genuiStats`) when the bound\n * store does not implement `genuiStats`.\n */\n @Get('genui')\n genui(@Query('from') from?: string, @Query('to') to?: string): Promise<GenuiStats> {\n return this.dashboard.genuiStats(resolveRange(from, to));\n }\n\n /** Most recent threads (default 50, max 200) with rolled-up counts. */\n @Get('threads')\n threads(@Query('limit') limit?: string): Promise<ThreadActivityRowWithLabel[]> {\n return this.dashboard.recentThreads(parseLimit(limit, 50));\n }\n\n /**\n * Paged, filterable threads for the Runs & tools threads table. `page` (default 1), `limit`\n * (default 25, max 200), and `where[actorRef]`/`where[title]` (case-insensitive substring)/\n * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.\n */\n @Get('threads-page')\n threadsPage(\n @Query('page') page?: string,\n @Query('limit') limit?: string,\n @Query('where') where?: Record<string, string>,\n ): Promise<GovernancePage<ThreadActivityRowWithLabel>> {\n const parsedWhere = parseThreadWhere(where);\n return this.dashboard.threadsPage({\n page: parsePageNumber(page),\n pageSize: parseLimit(limit, 25),\n ...(parsedWhere !== undefined ? { where: parsedWhere } : {}),\n });\n }\n\n /**\n * One thread drill-down: the thread (actor-labeled), its lifetime token/cost rollup, its newest\n * runs and its newest messages, in ONE round trip. `messages` (default 50, max 200) and `runs`\n * (default 25, max 200) cap the two lists; `runTotal` and `thread.messageCount` report the true\n * sizes so the console can say what the caps hid. 404s on an unknown thread id.\n *\n * A soft-deleted thread is still returned, flagged `deleted: true` — an audit needs the thread it\n * just lost, and the alternative is a drill-down that 404s on exactly the interesting case.\n */\n @Get('threads/:threadId')\n threadDetail(\n @Param('threadId') threadId: string,\n @Query('messages') messages?: string,\n @Query('runs') runs?: string,\n ): Promise<ThreadDetailWithLabel> {\n return this.dashboard.threadDetail({\n threadId,\n messageLimit: parseLimit(messages, 50),\n runLimit: parseLimit(runs, 25),\n });\n }\n\n /**\n * Current price row per model, for the pricing tab. 501s (via `DashboardService.listPrices`) when\n * no `AGENT_PRICING_STORE` is bound.\n */\n @Get('pricing')\n listPrices(): Promise<CurrentModelPrice[]> {\n return this.dashboard.listPrices();\n }\n\n /**\n * Set a model's current price. Body shape mirrors core's `ModelPriceInput`\n * (`{ modelId, inputPricePer1m, outputPricePer1m, cacheWritePricePer1m?, cacheReadPricePer1m? }`).\n * 501s when no `AGENT_PRICING_STORE` is bound; 400s on a malformed body.\n */\n @Post('pricing')\n upsertPrice(@Body() body: unknown): Promise<void> {\n return this.dashboard.upsertPrice(body);\n }\n\n /** Server-Sent Events stream of live `aviary:agent:*` events — the Live feed tails it. */\n @Sse('stream')\n stream(): Observable<{ data: LiveAgentEvent }> {\n return this.dashboard.streamEvents();\n }\n}\n","import { subscribe, unsubscribe } from 'node:diagnostics_channel';\nimport type {\n ActorSpendRow,\n AgentApprovalPort,\n AgentDiagnosticEvent,\n AgentGovernanceQueries,\n AgentPricingStore,\n ApprovalWhere,\n CurrentModelPrice,\n FeatureSpendRow,\n GenuiStats,\n GovernancePage,\n GovernancePageQuery,\n GovernanceRange,\n GovernanceRunDetail,\n GovernanceThreadDetail,\n GovernanceThreadDetailQuery,\n ModelSpendRow,\n PendingApprovalRow,\n RecentRunRow,\n RunAgentBreakdownRow,\n RunErrorBreakdownRow,\n RunMetrics,\n RunTrendPoint,\n RunWhere,\n ThreadActivityRow,\n ThreadSpendRow,\n ThreadWhere,\n ToolCallActivityRow,\n ToolCallWhere,\n ToolStatRow,\n UsageTrendPoint,\n} from '@dudousxd/nestjs-agent-core';\nimport { channelName } from '@dudousxd/nestjs-diagnostics';\nimport {\n Inject,\n Injectable,\n NotFoundException,\n NotImplementedException,\n Optional,\n} from '@nestjs/common';\nimport { Observable } from 'rxjs';\nimport type { ActorDirectory } from './actor-directory.js';\nimport { parseApprovalDecision } from './parse-approval-decision.js';\nimport { parsePriceInput } from './parse-price-input.js';\nimport {\n AGENT_ACTOR_DIRECTORY,\n AGENT_APPROVAL_PORT,\n AGENT_GOVERNANCE_QUERIES,\n AGENT_PRICING_STORE,\n} from './tokens.js';\n\n/** An actor-scoped row decorated with its resolved display label — `null` when unbound or unresolved. */\nexport interface WithActorLabel {\n actorLabel: string | null;\n}\n\nexport type ActorSpendRowWithLabel = ActorSpendRow & WithActorLabel;\nexport type ThreadSpendRowWithLabel = ThreadSpendRow & WithActorLabel;\nexport type ThreadActivityRowWithLabel = ThreadActivityRow & WithActorLabel;\n\n/**\n * `GET <api>/runs/:runId` — a run drill-down with the owning thread's `actorLabel` resolved, the\n * same way {@link threadsPage} labels its rows. The run row itself stays unlabeled, matching every\n * other run surface in this console.\n */\nexport type RunDetailWithLabel = Omit<GovernanceRunDetail, 'thread'> & {\n thread: GovernanceRunDetail['thread'] & WithActorLabel;\n};\n\n/** `GET <api>/threads/:threadId` — a thread drill-down, actor-labeled like {@link threadsPage}. */\nexport type ThreadDetailWithLabel = Omit<GovernanceThreadDetail, 'thread'> & {\n thread: ThreadActivityRowWithLabel;\n};\n\n/** The spend/usage overview the SPA renders on its headline section (`GET <api>/spend`). */\nexport interface SpendOverview {\n byModel: ModelSpendRow[];\n byActor: ActorSpendRowWithLabel[];\n trend: UsageTrendPoint[];\n /**\n * Spend per feature — agent turns and usage recorded outside them side by side. Absent when the\n * bound governance adapter has no `spendByFeature`.\n */\n byFeature?: FeatureSpendRow[];\n}\n\n/** The run-reliability overview the SPA renders on its Reliability section (`GET <api>/reliability`). */\nexport interface ReliabilityOverview {\n metrics: RunMetrics;\n byAgent: RunAgentBreakdownRow[];\n errors: RunErrorBreakdownRow[];\n trend: RunTrendPoint[];\n}\n\n/** The message returned as a 501 when no `AGENT_PRICING_STORE` is bound. */\nconst PRICING_STORE_UNBOUND_MESSAGE =\n 'Pricing CRUD is unavailable: no AGENT_PRICING_STORE is bound. Bind a pricing store (e.g. ' +\n 'MikroOrmPricingStore from @dudousxd/nestjs-agent-store-mikro-orm) to enable it.';\n\n/** The message returned as a 501 when no `AGENT_APPROVAL_PORT` is bound. */\nconst APPROVAL_PORT_UNBOUND_MESSAGE =\n 'Approve/reject is unavailable: no AGENT_APPROVAL_PORT is bound. Import AgentModule from ' +\n '@dudousxd/nestjs-agent alongside this dashboard to enable it — the approvals inbox stays ' +\n 'read-only until then.';\n\n/** The message returned as a 501 when the bound read-model does not implement `genuiStats`. */\nconst GENUI_STATS_UNSUPPORTED_MESSAGE =\n 'Generative UI telemetry is unavailable: the bound AGENT_GOVERNANCE_QUERIES store does not ' +\n 'implement genuiStats. Use a store adapter that records generative-UI calls (recordGenuiCalls) ' +\n 'to enable it.';\n\n/** One live agent event forwarded over SSE, flattened from the `aviary:agent:*` diagnostics envelope. */\nexport interface LiveAgentEvent {\n /** The event name, e.g. `run.started` / `tool-call` / `quota.exceeded`. */\n event: string;\n /** Epoch millis the event was emitted. */\n ts: number;\n /** The library-defined payload (see the `Agent*Event` shapes in core's diagnostics). */\n payload: Record<string, unknown>;\n}\n\n/**\n * The `aviary:agent:*` events the Live feed tails — a curated subset of core's\n * `AGENT_DIAGNOSTIC_EVENTS`, typed against it so a renamed event is a compile error here. Every run\n * TERMINAL is in it (finished, failed, cancelled): a feed that showed runs starting but never failing\n * would read as \"all fine\" exactly when it is not. The per-turn bookkeeping events (skills, memory,\n * retrieval, tool retries) are left to the telescope.\n */\nconst AGENT_EVENTS: readonly AgentDiagnosticEvent[] = [\n 'run.started',\n 'message',\n 'tool-call',\n 'quota.exceeded',\n 'run.finished',\n 'run.failed',\n 'run.cancelled',\n 'delegated',\n];\n\n/** The `node:diagnostics_channel` envelope `emit()` publishes (see `@dudousxd/nestjs-diagnostics`). */\ninterface AgentDiagnosticEnvelope {\n event: string;\n ts?: number;\n payload?: Record<string, unknown>;\n}\n\n/** Narrow the untyped diagnostics-channel message to the envelope we forward. */\nfunction isAgentEnvelope(message: unknown): message is AgentDiagnosticEnvelope {\n return (\n typeof message === 'object' &&\n message !== null &&\n 'event' in message &&\n typeof (message as { event: unknown }).event === 'string'\n );\n}\n\n/**\n * Read-model + live bridge backing the AI-gateway console.\n *\n * - Historical, restart-surviving spend/usage/threads come from the injected\n * {@link AGENT_GOVERNANCE_QUERIES} read-model (backed by a store adapter). The host must provide\n * that token — bind it via your `@dudousxd/nestjs-agent` module (global) alongside this dashboard.\n * - Live activity comes off the `aviary:agent:*` diagnostics channel, subscribed per SSE client and\n * unsubscribed when the client disconnects.\n * - `actorLabel` on actor-scoped rows comes from the OPTIONAL {@link AGENT_ACTOR_DIRECTORY} — `null`\n * on every row when nothing is bound, so the console degrades to raw `actorRef`s instead of failing.\n * - Pricing CRUD (`listPrices`/`upsertPrice`) reads/writes the OPTIONAL {@link AGENT_PRICING_STORE} —\n * a 501 with a clear message when nothing is bound.\n */\n@Injectable()\nexport class DashboardService {\n constructor(\n @Inject(AGENT_GOVERNANCE_QUERIES) private readonly queries: AgentGovernanceQueries,\n @Optional()\n @Inject(AGENT_ACTOR_DIRECTORY)\n private readonly actorDirectory?: ActorDirectory,\n @Optional()\n @Inject(AGENT_PRICING_STORE)\n private readonly pricingStore?: AgentPricingStore,\n @Optional()\n @Inject(AGENT_APPROVAL_PORT)\n private readonly approvalPort?: AgentApprovalPort,\n ) {}\n\n /** Spend/usage overview for a day range: by-model + by-actor spend and the daily trend, in parallel. */\n async spend(range: GovernanceRange): Promise<SpendOverview> {\n const [byModel, byActorRaw, trend, byFeature] = await Promise.all([\n this.queries.spendByModel(range),\n this.queries.spendByActor(range),\n this.queries.usageTrend(range),\n this.queries.spendByFeature?.(range),\n ]);\n const byActor = await this.withActorLabels(byActorRaw);\n return { byModel, byActor, trend, ...(byFeature !== undefined ? { byFeature } : {}) };\n }\n\n /** Top threads by cost for a day range (default 10, highest cost first). */\n async topThreads(range: GovernanceRange, limit = 10): Promise<ThreadSpendRowWithLabel[]> {\n const rows = await this.queries.spendByThread(range, limit);\n return this.withActorLabels(rows);\n }\n\n /** Run reliability for a day range: metrics, by-agent/by-error breakdowns and the trend, in parallel. */\n async reliability(range: GovernanceRange): Promise<ReliabilityOverview> {\n const [metrics, byAgent, errors, trend] = await Promise.all([\n this.queries.runMetrics(range),\n this.queries.runsByAgent(range),\n this.queries.runErrors(range),\n this.queries.runTrend(range),\n ]);\n return { metrics, byAgent, errors, trend };\n }\n\n /** Most recent runs (status/agent/duration/error) for the Reliability recent-runs table. */\n recentRuns(limit: number): Promise<RecentRunRow[]> {\n return this.queries.recentRuns(limit);\n }\n\n /** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */\n recentToolCalls(limit: number): Promise<ToolCallActivityRow[]> {\n return this.queries.recentToolCalls(limit);\n }\n\n /** Paged, filterable tool calls for the Runs & tools activity table. */\n toolCallsPage(\n query: GovernancePageQuery<ToolCallWhere>,\n ): Promise<GovernancePage<ToolCallActivityRow>> {\n return this.queries.toolCallsPage(query);\n }\n\n /** Paged, filterable runs for the Reliability recent-runs table. */\n runsPage(query: GovernancePageQuery<RunWhere>): Promise<GovernancePage<RecentRunRow>> {\n return this.queries.runsPage(query);\n }\n\n /**\n * Tool calls sitting `pending_approval`, oldest first, for the cross-thread approvals inbox.\n * Capped and totalless — kept because the SPA still calls `GET approvals`; new callers want\n * {@link approvalsPage}, which reports how much of the backlog the cap hid.\n */\n pendingApprovals(limit: number): Promise<PendingApprovalRow[]> {\n return this.queries.pendingApprovals(limit);\n }\n\n /** Paged, filterable approvals inbox with a total — the backlog-safe read of the HITL queue. */\n approvalsPage(\n query: GovernancePageQuery<ApprovalWhere>,\n ): Promise<GovernancePage<PendingApprovalRow>> {\n return this.queries.approvalsPage(query);\n }\n\n /**\n * One run with its thread and its tool calls. 404s on an unknown id rather than returning an empty\n * detail: \"this run does not exist\" and \"this run did nothing\" are different answers, and a console\n * that conflates them sends an operator looking for a bug that isn't there.\n */\n async runDetail(runId: string): Promise<RunDetailWithLabel> {\n const detail = await this.queries.runDetail(runId);\n if (detail === null) {\n throw new NotFoundException(`No run \"${runId}\".`);\n }\n const [thread] = await this.withActorLabels([detail.thread]);\n // `withActorLabels` returns one row per input; the array is never empty here.\n return { ...detail, thread: thread ?? { ...detail.thread, actorLabel: null } };\n }\n\n /** One thread with its usage rollup, runs and messages. 404s on an unknown id, like {@link runDetail}. */\n async threadDetail(query: GovernanceThreadDetailQuery): Promise<ThreadDetailWithLabel> {\n const detail = await this.queries.threadDetail(query);\n if (detail === null) {\n throw new NotFoundException(`No thread \"${query.threadId}\".`);\n }\n const [thread] = await this.withActorLabels([detail.thread]);\n return { ...detail, thread: thread ?? { ...detail.thread, actorLabel: null } };\n }\n\n /** Per-tool call/failure/rejection/latency rollup for a day range, for the Tools section. */\n toolStats(range: GovernanceRange): Promise<ToolStatRow[]> {\n return this.queries.toolStats(range);\n }\n\n /**\n * Generative-UI telemetry for a day range — first-try success per tool/model, common errors, UI\n * cost per day/turn and time-to-frame. `genuiStats` is OPTIONAL on the read-model (a store that\n * does not record UI calls leaves it out), so an unsupported store 501s with a clear message, the\n * same posture as an unbound pricing store — the console renders that as its own state.\n */\n async genuiStats(range: GovernanceRange): Promise<GenuiStats> {\n if (this.queries.genuiStats === undefined) {\n throw new NotImplementedException(GENUI_STATS_UNSUPPORTED_MESSAGE);\n }\n return this.queries.genuiStats(range);\n }\n\n /**\n * Decide a pending HITL tool call from the console. Routes through the OPTIONAL\n * {@link AGENT_APPROVAL_PORT} — bound by `@dudousxd/nestjs-agent` to the SAME signal path chat\n * approvals use — so a 501 here (checked BEFORE body validation, same ordering as\n * {@link upsertPrice}) means \"no approval port bound\", not \"your body was invalid\". `executedByRef`\n * is an OPAQUE decider ref the caller resolved from the live request (see\n * `AgentDashboardOptions.approvalActorRef`); omitted when the host didn't configure one.\n */\n async decideApproval(\n toolCallId: string,\n body: unknown,\n executedByRef: string | undefined,\n ): Promise<void> {\n if (this.approvalPort === undefined) {\n throw new NotImplementedException(APPROVAL_PORT_UNBOUND_MESSAGE);\n }\n const decision = parseApprovalDecision(body);\n const opts = executedByRef !== undefined ? { executedByRef } : {};\n if (decision.approved) {\n await this.approvalPort.approve(toolCallId, opts);\n return;\n }\n await this.approvalPort.reject(toolCallId, {\n ...opts,\n ...(decision.reason !== undefined ? { reason: decision.reason } : {}),\n });\n }\n\n /** Most recent threads with rolled-up message/token counts. */\n async recentThreads(limit: number): Promise<ThreadActivityRowWithLabel[]> {\n const rows = await this.queries.recentThreads(limit);\n return this.withActorLabels(rows);\n }\n\n /**\n * Paged, filterable threads for the Runs & tools threads table — actor-labeled the same way\n * {@link recentThreads} is.\n */\n async threadsPage(\n query: GovernancePageQuery<ThreadWhere>,\n ): Promise<GovernancePage<ThreadActivityRowWithLabel>> {\n const page = await this.queries.threadsPage(query);\n const rows = await this.withActorLabels(page.rows);\n return { ...page, rows };\n }\n\n /**\n * Decorate actor-scoped rows with `actorLabel`, batching the distinct `actorRef`s into ONE\n * {@link ActorDirectory.resolveDisplay} call per response. `null` for every row when no directory\n * is bound, or for a ref the directory didn't resolve.\n */\n private async withActorLabels<Row extends { actorRef: string }>(\n rows: Row[],\n ): Promise<(Row & WithActorLabel)[]> {\n if (rows.length === 0) {\n return [];\n }\n if (this.actorDirectory === undefined) {\n return rows.map((row) => ({ ...row, actorLabel: null }));\n }\n const refs = [...new Set(rows.map((row) => row.actorRef))];\n const resolved = await this.actorDirectory.resolveDisplay(refs);\n return rows.map((row) => ({ ...row, actorLabel: resolved[row.actorRef] ?? null }));\n }\n\n /** Current price row per model, for the pricing tab. 501s when no `AGENT_PRICING_STORE` is bound. */\n async listPrices(): Promise<CurrentModelPrice[]> {\n if (this.pricingStore === undefined) {\n throw new NotImplementedException(PRICING_STORE_UNBOUND_MESSAGE);\n }\n return this.pricingStore.listCurrentPrices();\n }\n\n /**\n * Set a model's current price (`POST <api>/pricing` body). 501s when no `AGENT_PRICING_STORE` is\n * bound (checked BEFORE body validation, so an unbound store always reports as unimplemented rather\n * than as a validation error); otherwise the body is minimally validated via {@link parsePriceInput}.\n */\n async upsertPrice(body: unknown): Promise<void> {\n if (this.pricingStore === undefined) {\n throw new NotImplementedException(PRICING_STORE_UNBOUND_MESSAGE);\n }\n await this.pricingStore.upsertModelPrice(parsePriceInput(body));\n }\n\n /**\n * Live SSE stream of `aviary:agent:*` diagnostics events. One subscription per SSE client:\n * subscribing wires a handler onto each agent channel; the returned teardown removes them all when\n * the client disconnects (or the observable is otherwise unsubscribed).\n */\n streamEvents(): Observable<{ data: LiveAgentEvent }> {\n return new Observable<{ data: LiveAgentEvent }>((subscriber) => {\n const bindings = AGENT_EVENTS.map((event) => {\n const name = channelName('agent', event);\n const handler = (message: unknown): void => {\n if (!isAgentEnvelope(message)) return;\n subscriber.next({\n data: {\n event: message.event,\n ts: message.ts ?? Date.now(),\n payload: message.payload ?? {},\n },\n });\n };\n subscribe(name, handler);\n return { name, handler };\n });\n return () => {\n for (const binding of bindings) unsubscribe(binding.name, binding.handler);\n };\n });\n }\n}\n","import { BadRequestException } from '@nestjs/common';\n\n/** The parsed, minimally-validated `POST <api>/approvals/:toolCallId` body. */\nexport interface ApprovalDecisionInput {\n approved: boolean;\n reason?: string;\n}\n\n/**\n * Minimal shape guard for a `POST <api>/approvals/:toolCallId` body — rejects junk before it reaches\n * `AgentApprovalPort.approve`/`reject`. Mirrors `parsePriceInput`'s role for the pricing endpoint.\n */\nexport function parseApprovalDecision(body: unknown): ApprovalDecisionInput {\n if (typeof body !== 'object' || body === null) {\n throw new BadRequestException('Expected a JSON object body.');\n }\n const { approved, reason } = body as Record<string, unknown>;\n\n if (typeof approved !== 'boolean') {\n throw new BadRequestException('\"approved\" must be a boolean.');\n }\n if (reason !== undefined && typeof reason !== 'string') {\n throw new BadRequestException('\"reason\" must be a string when present.');\n }\n\n return { approved, ...(reason !== undefined ? { reason } : {}) };\n}\n","import type { ModelPriceInput } from '@dudousxd/nestjs-agent-core';\nimport { BadRequestException } from '@nestjs/common';\n\n/**\n * Minimal shape guard for a `POST <api>/pricing` body — rejects junk before it reaches\n * `AgentPricingStore.upsertModelPrice`. Not a full schema validator (the store adapter owns real\n * constraints, e.g. uniqueness); this only checks the wire shape core's `ModelPriceInput` requires.\n */\nexport function parsePriceInput(body: unknown): ModelPriceInput {\n if (typeof body !== 'object' || body === null) {\n throw new BadRequestException('Expected a JSON object body.');\n }\n const { modelId, inputPricePer1m, outputPricePer1m, cacheWritePricePer1m, cacheReadPricePer1m } =\n body as Record<string, unknown>;\n\n if (typeof modelId !== 'string' || modelId.trim().length === 0) {\n throw new BadRequestException('\"modelId\" must be a non-empty string.');\n }\n if (!isFiniteNonNegative(inputPricePer1m)) {\n throw new BadRequestException('\"inputPricePer1m\" must be a non-negative number.');\n }\n if (!isFiniteNonNegative(outputPricePer1m)) {\n throw new BadRequestException('\"outputPricePer1m\" must be a non-negative number.');\n }\n if (cacheWritePricePer1m !== undefined && !isFiniteNonNegative(cacheWritePricePer1m)) {\n throw new BadRequestException(\n '\"cacheWritePricePer1m\" must be a non-negative number when present.',\n );\n }\n if (cacheReadPricePer1m !== undefined && !isFiniteNonNegative(cacheReadPricePer1m)) {\n throw new BadRequestException(\n '\"cacheReadPricePer1m\" must be a non-negative number when present.',\n );\n }\n\n return {\n modelId,\n inputPricePer1m,\n outputPricePer1m,\n ...(cacheWritePricePer1m !== undefined ? { cacheWritePricePer1m } : {}),\n ...(cacheReadPricePer1m !== undefined ? { cacheReadPricePer1m } : {}),\n };\n}\n\nfunction isFiniteNonNegative(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0;\n}\n","/**\n * DI tokens for the standalone AI-gateway dashboard.\n *\n * All use `Symbol.for(...)` (the global symbol registry) on purpose: pnpm peer multiplexing + dual\n * ESM/CJS can load a package more than once, and a plain `Symbol()` would mint a distinct token per\n * copy and break DI across the ESM/CJS split. A registered symbol collapses every copy onto the same\n * token.\n */\n\n/**\n * The governance read-model, owned by `@dudousxd/nestjs-agent-core`. We re-declare it here BY VALUE\n * (not by import) so DI does not depend on a runtime value-import of core resolving — `Symbol.for`\n * with the identical key resolves to the SAME symbol instance as core's own\n * `packages/core/src/tokens.ts` export. The key MUST stay byte-identical with that export.\n */\nexport const AGENT_GOVERNANCE_QUERIES = Symbol.for('@dudousxd/nestjs-agent:governance-queries');\n\n/**\n * Optional actor→label resolver (see {@link ActorDirectory} in `./actor-directory.js`), owned by\n * `@dudousxd/nestjs-agent-core`. Re-declared here BY VALUE for the same reason as\n * {@link AGENT_GOVERNANCE_QUERIES} above — the key MUST stay byte-identical with core's own\n * `AGENT_ACTOR_DIRECTORY` export so both copies collapse onto the same registered symbol. Optional:\n * the dashboard works with actorRef-only rows when nothing is bound.\n */\nexport const AGENT_ACTOR_DIRECTORY = Symbol.for('@dudousxd/nestjs-agent:actor-directory');\n\n/**\n * The pricing WRITE side (`AgentPricingStore`), owned by `@dudousxd/nestjs-agent-core`. Re-declared\n * here BY VALUE for the same reason as {@link AGENT_GOVERNANCE_QUERIES} above. Optional: the pricing\n * tab/endpoints 501 with a clear message when nothing is bound.\n */\nexport const AGENT_PRICING_STORE = Symbol.for('@dudousxd/nestjs-agent:pricing-store');\n\n/**\n * Console-side HITL decisions (`AgentApprovalPort`), owned by `@dudousxd/nestjs-agent-core`.\n * Re-declared here BY VALUE for the same reason as {@link AGENT_GOVERNANCE_QUERIES} above. Optional:\n * bound by `@dudousxd/nestjs-agent` (which adapts it to the same signal path chat approvals use) —\n * absent, `POST <api>/approvals/:toolCallId` 501s and the approvals inbox renders read-only.\n */\nexport const AGENT_APPROVAL_PORT = Symbol.for('@dudousxd/nestjs-agent:approval-port');\n\n/**\n * The app's request-identity seam (`ActorResolver`), owned by `@dudousxd/nestjs-agent-core` and\n * bound + exported by the (global) `AgentModule` from the host's `actorResolver` option. Re-declared\n * here BY VALUE for the same reason as {@link AGENT_GOVERNANCE_QUERIES} above. Optional: the default\n * decider attribution for `POST <api>/approvals/:toolCallId` — absent (or throwing, i.e.\n * unauthenticated), `executedByRef` is simply omitted.\n */\nexport const AGENT_ACTOR_RESOLVER = Symbol.for('@dudousxd/nestjs-agent:actor-resolver');\n\n/** DI token carrying the UI mount base (e.g. `/ai-gateway`). */\nexport const DASHBOARD_BASE_PATH = Symbol.for('@dudousxd/nestjs-agent-dashboard:base-path');\n\n/** DI token carrying the JSON API base the SPA fetches from (e.g. `/ai-gateway/api`). */\nexport const DASHBOARD_API_PATH = Symbol.for('@dudousxd/nestjs-agent-dashboard:api-path');\n\n/**\n * DI token carrying the host-provided {@link AgentDashboardOptions.approvalActorRef} extractor (or\n * `undefined` when the host didn't set one — the controller then falls back to\n * {@link AGENT_ACTOR_RESOLVER} for decider attribution). Threaded to `AgentApiModule` (where the API\n * controller actually lives) same as the pattern above — `useValue` even when `undefined`, so\n * injecting it needs no `@Optional()`.\n */\nexport const DASHBOARD_APPROVAL_ACTOR_REF = Symbol.for(\n '@dudousxd/nestjs-agent-dashboard:approval-actor-ref',\n);\n\n/** DI token carrying the console's `authorize` (and whether to warn when it is omitted). */\nexport const DASHBOARD_CONSOLE_OPTIONS = Symbol.for(\n '@dudousxd/nestjs-agent-dashboard:console-options',\n);\n\n/** DI token: whether the agent's dev tools register into the dev-tools slot. */\nexport const DASHBOARD_DEV_TOOLS = Symbol.for('@dudousxd/nestjs-agent-dashboard:dev-tools');\n","import type {\n ApprovalWhere,\n RunWhere,\n ThreadWhere,\n ToolCallWhere,\n} from '@dudousxd/nestjs-agent-core';\nimport { BadRequestException } from '@nestjs/common';\n\nconst ISO_DAY = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Parse a `page` query param (1-based); falls back to 1 when absent or not a positive integer. */\nexport function parsePageNumber(value: string | undefined): number {\n const parsed = value === undefined ? Number.NaN : Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed < 1) return 1;\n return parsed;\n}\n\n/** Rejects a `where[<field>]` whose field name isn't in `allowed`, naming the offending field. */\nfunction assertKnownFields(raw: Record<string, string>, allowed: readonly string[]): void {\n for (const field of Object.keys(raw)) {\n if (!allowed.includes(field)) {\n throw new BadRequestException(`Unknown where field \"${field}\".`);\n }\n }\n}\n\n/** Rejects a day-bound `where[<field>]` that isn't `YYYY-MM-DD`, mirroring the range endpoints' check. */\nfunction assertDayFields(raw: Record<string, string>, dayFields: readonly string[]): void {\n for (const field of dayFields) {\n const value = raw[field];\n if (value !== undefined && !ISO_DAY.test(value)) {\n throw new BadRequestException(`\"where[${field}]\" must be YYYY-MM-DD.`);\n }\n }\n}\n\nconst DAY_FIELDS = ['fromDay', 'toDay'] as const;\nconst TOOL_CALL_WHERE_FIELDS = [\n 'toolName',\n 'toolType',\n 'status',\n 'threadId',\n 'fromDay',\n 'toDay',\n] as const;\n\n/**\n * Parse `GET tool-calls-page`'s `where[...]` query params into a `ToolCallWhere`. `undefined` (no\n * `where` params at all) means no filter. Throws `BadRequestException` on an unknown field name or a\n * malformed day bound.\n */\nexport function parseToolCallWhere(\n raw: Record<string, string> | undefined,\n): ToolCallWhere | undefined {\n if (raw === undefined || Object.keys(raw).length === 0) return undefined;\n assertKnownFields(raw, TOOL_CALL_WHERE_FIELDS);\n assertDayFields(raw, DAY_FIELDS);\n return {\n ...(raw.toolName !== undefined ? { toolName: raw.toolName } : {}),\n ...(raw.toolType !== undefined ? { toolType: raw.toolType } : {}),\n ...(raw.status !== undefined ? { status: raw.status } : {}),\n ...(raw.threadId !== undefined ? { threadId: raw.threadId } : {}),\n ...(raw.fromDay !== undefined ? { fromDay: raw.fromDay } : {}),\n ...(raw.toDay !== undefined ? { toDay: raw.toDay } : {}),\n };\n}\n\nconst THREAD_WHERE_FIELDS = ['actorRef', 'title', 'fromDay', 'toDay'] as const;\n\n/**\n * Parse `GET threads-page`'s `where[...]` query params into a `ThreadWhere`. `undefined` means no\n * filter. Throws `BadRequestException` on an unknown field name or a malformed day bound.\n */\nexport function parseThreadWhere(raw: Record<string, string> | undefined): ThreadWhere | undefined {\n if (raw === undefined || Object.keys(raw).length === 0) return undefined;\n assertKnownFields(raw, THREAD_WHERE_FIELDS);\n assertDayFields(raw, DAY_FIELDS);\n return {\n ...(raw.actorRef !== undefined ? { actorRef: raw.actorRef } : {}),\n ...(raw.title !== undefined ? { title: raw.title } : {}),\n ...(raw.fromDay !== undefined ? { fromDay: raw.fromDay } : {}),\n ...(raw.toDay !== undefined ? { toDay: raw.toDay } : {}),\n };\n}\n\n// `threadId` was supported by every adapter's `RunWhere` but never accepted here, so \"show me this\n// thread's runs\" — the drill-down's most obvious follow-up query — 400'd with \"Unknown where field\".\nconst RUN_WHERE_FIELDS = [\n 'agentName',\n 'status',\n 'errorCode',\n 'threadId',\n 'fromDay',\n 'toDay',\n] as const;\n\n/**\n * Parse `GET runs-page`'s `where[...]` query params into a `RunWhere`. `undefined` means no filter.\n * Throws `BadRequestException` on an unknown field name or a malformed day bound.\n */\nexport function parseRunWhere(raw: Record<string, string> | undefined): RunWhere | undefined {\n if (raw === undefined || Object.keys(raw).length === 0) return undefined;\n assertKnownFields(raw, RUN_WHERE_FIELDS);\n assertDayFields(raw, DAY_FIELDS);\n return {\n ...(raw.agentName !== undefined ? { agentName: raw.agentName } : {}),\n ...(raw.status !== undefined ? { status: raw.status } : {}),\n ...(raw.errorCode !== undefined ? { errorCode: raw.errorCode } : {}),\n ...(raw.threadId !== undefined ? { threadId: raw.threadId } : {}),\n ...(raw.fromDay !== undefined ? { fromDay: raw.fromDay } : {}),\n ...(raw.toDay !== undefined ? { toDay: raw.toDay } : {}),\n };\n}\n\nconst APPROVAL_WHERE_FIELDS = [\n 'toolName',\n 'threadId',\n 'actorRef',\n 'agentName',\n 'fromDay',\n 'toDay',\n] as const;\n\n/**\n * Parse `GET approvals-page`'s `where[...]` query params into an `ApprovalWhere`. `undefined` means\n * no filter. Throws `BadRequestException` on an unknown field name or a malformed day bound.\n */\nexport function parseApprovalWhere(\n raw: Record<string, string> | undefined,\n): ApprovalWhere | undefined {\n if (raw === undefined || Object.keys(raw).length === 0) return undefined;\n assertKnownFields(raw, APPROVAL_WHERE_FIELDS);\n assertDayFields(raw, DAY_FIELDS);\n return {\n ...(raw.toolName !== undefined ? { toolName: raw.toolName } : {}),\n ...(raw.threadId !== undefined ? { threadId: raw.threadId } : {}),\n ...(raw.actorRef !== undefined ? { actorRef: raw.actorRef } : {}),\n ...(raw.agentName !== undefined ? { agentName: raw.agentName } : {}),\n ...(raw.fromDay !== undefined ? { fromDay: raw.fromDay } : {}),\n ...(raw.toDay !== undefined ? { toDay: raw.toDay } : {}),\n };\n}\n","/**\n * Leading slash, no trailing slash (`'ai-gateway/'` -> `'/ai-gateway'`). Shared by\n * {@link AgentDashboardModule.forRoot} and {@link agentDashboardMountPaths} so the mount-path math\n * behind the module and the pure helper that mirrors it can never drift apart.\n */\nexport function normalizeDashboardPath(path: string): string {\n return `/${path.replace(/^\\/+|\\/+$/g, '')}`;\n}\n","import { normalizeDashboardPath } from './normalize-path.js';\n\n/** Same shape {@link AgentDashboardModule.forRoot} accepts — kept local so this stays a pure, DI-free helper. */\nexport interface AgentDashboardMountPathsOptions {\n basePath?: string;\n apiBasePath?: string;\n}\n\n/** Strip the leading slash `normalizeDashboardPath` adds — `setGlobalPrefix`'s `exclude` roots are unprefixed. */\nfunction unprefixed(path: string): string {\n return path.replace(/^\\/+/, '');\n}\n\n/**\n * Route roots a host must EXCLUDE from a global prefix (`setGlobalPrefix('api', { exclude })`) so\n * the AI-gateway dashboard's SPA and JSON API keep resolving at their configured mount paths instead\n * of being shifted under the prefix.\n *\n * Unlike a single-surface dashboard (e.g. `telescopeMountPaths()`), this one mounts TWO route roots —\n * the UI at `basePath` and its JSON API at `apiBasePath` — so excluding only one leaves the other\n * shadowed. `options` mirrors {@link AgentDashboardOptions} and resolves through the exact same\n * defaulting (`apiBasePath` falls back to `<basePath>/api`) as {@link AgentDashboardModule.forRoot},\n * so the excluded roots always agree with what actually got mounted.\n *\n * @example\n * ```ts\n * // Raw defaults (basePath `/ai-gateway`, apiBasePath `/ai-gateway/api`):\n * app.setGlobalPrefix('api', { exclude: agentDashboardMountPaths() });\n * // -> ['ai-gateway', 'ai-gateway/{*splat}', 'ai-gateway/api', 'ai-gateway/api/{*splat}']\n *\n * // The recommended pattern — apiBasePath nested under the app's own `/api` prefix — MUST pass the\n * // same options given to `forRoot(...)`:\n * const dashboardOptions = { apiBasePath: '/api/ai-gateway' };\n * app.setGlobalPrefix('api', { exclude: agentDashboardMountPaths(dashboardOptions) });\n * // -> ['ai-gateway', 'ai-gateway/{*splat}', 'api/ai-gateway', 'api/ai-gateway/{*splat}']\n * ```\n */\nexport function agentDashboardMountPaths(options?: AgentDashboardMountPathsOptions): string[] {\n const basePath = normalizeDashboardPath(options?.basePath ?? '/ai-gateway');\n const apiBasePath = normalizeDashboardPath(options?.apiBasePath ?? `${basePath}/api`);\n const base = unprefixed(basePath);\n const api = unprefixed(apiBasePath);\n return [base, `${base}/{*splat}`, api, `${api}/{*splat}`];\n}\n","import { withControllerDecorators } from '@dudousxd/nestjs-agent-core';\nimport 'reflect-metadata';\nimport { type DynamicModule, Module, UseFilters, UseGuards } from '@nestjs/common';\nimport { RouterModule } from '@nestjs/core';\nimport { AgentApiController } from './agent-api.controller.js';\nimport { AgentUiController } from './agent-ui.controller.js';\nimport {\n ConsoleAuthorizeFilter,\n ConsoleAuthorizeGuard,\n DashboardConsoleAccess,\n type DashboardConsoleOptions,\n} from './console-authorize.guard.js';\nimport type { ConsoleAuthorize } from './console-authorize.js';\nimport { DashboardService } from './dashboard.service.js';\nimport { AgentDevToolsRegistration } from './dev-tools.js';\nimport { normalizeDashboardPath } from './normalize-path.js';\nimport {\n DASHBOARD_API_PATH,\n DASHBOARD_APPROVAL_ACTOR_REF,\n DASHBOARD_BASE_PATH,\n DASHBOARD_CONSOLE_OPTIONS,\n DASHBOARD_DEV_TOOLS,\n} from './tokens.js';\n\n/**\n * `TReq` mirrors core's `ActorResolver<TReq>` convention: the transport request type\n * {@link AgentDashboardOptions.approvalActorRef} receives. Defaults to `unknown`.\n */\nexport interface AgentDashboardOptions<TReq = unknown> {\n /**\n * Where the SPA (UI) is served. Default `/ai-gateway`. This is a page route — keep it out of an\n * `/api` prefix so it reads as a UI, not an endpoint.\n */\n basePath?: string;\n /**\n * Where the JSON API is mounted (what the SPA fetches). Default `<basePath>/api`. Set it under\n * your app's `/api` prefix — e.g. `/api/ai-gateway` — so the API inherits the app's auth/proxy\n * rules while the UI stays at `basePath`.\n */\n apiBasePath?: string;\n /**\n * Who reaches the console (the SPA and every API/SSE route): an inline function\n * `(ctx) => boolean | { allowed } | { redirectTo }`, an `@Injectable()` `ConsoleAuthorizer` class,\n * a lazy import of one, `{ policy, ability }` (`@nestjs/authorization`), or a gate name\n * (`@dudousxd/nestjs-authz`). `authorizeByToken({ token })` checks a bearer token.\n *\n * Omitted: open outside production; in production every request answers 403 (and a warning is\n * logged at boot).\n */\n authorize?: ConsoleAuthorize;\n /**\n * Class decorators applied to every controller this module mounts, before the console's own\n * `authorize` check — e.g. `[UseGuards(AuthenticationGuard)]` so `request.user` is set when\n * `authorize` runs. Applied to a per-registration subclass, so nothing sticks to the shared\n * classes. They do not replace `authorize`. Static wiring.\n */\n decorators?: ClassDecorator[];\n /**\n * `false` mounts none of the console's routes, while its services still run — the way NestJS's\n * own packages leave routes to you: serve them from controllers of your own, protected with the\n * official tools, by extending the exported `AgentApiController` / `AgentUiController` under your\n * own path and decorators (the module exports what they inject); `UseConsoleAuthorize()` applies\n * this module's `authorize` to them. Static wiring.\n */\n routes?: boolean;\n /** Extra `imports` merged into the console's modules — what a decorator's guard injects. */\n imports?: DynamicModule['imports'];\n /**\n * OVERRIDE for WHO is deciding a HITL approval — invoked on the incoming\n * `POST <api>/approvals/:toolCallId` request to stamp `AgentApprovalPort`'s `opts.executedByRef`.\n *\n * DEFAULT (option omitted): the AgentModule-configured actor resolver (`AGENT_ACTOR_RESOLVER`) is\n * consulted — the same identity seam chat requests use — and its actor id becomes the decider ref\n * (a throwing resolver, i.e. an unauthenticated request, just omits the ref). Set this only when\n * the console's caller is identified differently from chat's; returning `undefined` leaves\n * `executedByRef` unset — the explicit override wins outright, with no resolver fallback.\n */\n approvalActorRef?: (req: TReq) => string | undefined;\n /**\n * Register the console's reads as developer tools on the agent MCP server's dev endpoint\n * (`/mcp/aviary`), gated by this console's `authorize`. Default: on when the console's routes are\n * mounted, off with `routes: false`.\n */\n devTools?: boolean;\n}\n\n/** Leading slash, no trailing slash. */\nfunction normalize(path: string): string {\n return normalizeDashboardPath(path);\n}\n\n/** The decorators every mounted controller gets: the app's, then the console's access check. */\nfunction consoleDecorators(decorators: ClassDecorator[] | undefined): ClassDecorator[] {\n return [\n ...(decorators ?? []),\n UseGuards(ConsoleAuthorizeGuard),\n UseFilters(ConsoleAuthorizeFilter),\n ];\n}\n\n/** Holds the JSON API + SSE controller and its read service, mounted on its own path. */\n@Module({})\nexport class AgentApiModule {\n static register<TReq = unknown>(options: {\n imports?: DynamicModule['imports'];\n decorators?: ClassDecorator[];\n routes?: boolean;\n approvalActorRef?: (req: TReq) => string | undefined;\n authorize?: ConsoleAuthorize;\n devTools?: boolean;\n }): DynamicModule {\n // The warning for an omitted `authorize` logs with `routes: false` too: the exported guard still\n // answers 403 in production.\n const consoleOptions: DashboardConsoleOptions = {\n ...(options.authorize !== undefined ? { authorize: options.authorize } : {}),\n };\n return {\n module: AgentApiModule,\n imports: [...(options.imports ?? [])],\n controllers:\n options.routes === false\n ? []\n : withControllerDecorators([AgentApiController], consoleDecorators(options.decorators)),\n providers: [\n DashboardService,\n // `useValue` even when `options.approvalActorRef` is `undefined` — AgentApiController\n // injects this WITHOUT `@Optional()`.\n { provide: DASHBOARD_APPROVAL_ACTOR_REF, useValue: options.approvalActorRef },\n { provide: DASHBOARD_CONSOLE_OPTIONS, useValue: consoleOptions },\n DashboardConsoleAccess,\n { provide: DASHBOARD_DEV_TOOLS, useValue: options.devTools ?? options.routes !== false },\n AgentDevToolsRegistration,\n ],\n // What a self-mounted `AgentApiController` subclass injects, too (`routes: false`), and the\n // access check `UseConsoleAuthorize()` runs.\n exports: [DashboardService, DASHBOARD_APPROVAL_ACTOR_REF, DashboardConsoleAccess],\n };\n }\n}\n\n/**\n * The AI-gateway console: the SPA at `basePath` and its JSON API at `apiBasePath`. Who reaches them\n * is `authorize` (see {@link AgentDashboardOptions.authorize}).\n */\n@Module({})\nexport class AgentDashboardModule {\n static forRoot<TReq = unknown>(options: AgentDashboardOptions<TReq> = {}): DynamicModule {\n const basePath = normalize(options.basePath ?? '/ai-gateway');\n const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);\n return {\n module: AgentDashboardModule,\n imports: [\n ...(options.imports ?? []),\n // Spread-only-when-set: exactOptionalPropertyTypes rejects an explicit `undefined`.\n AgentApiModule.register({\n ...(options.imports ? { imports: options.imports } : {}),\n ...(options.decorators ? { decorators: options.decorators } : {}),\n ...(options.routes === false ? { routes: false } : {}),\n ...(options.approvalActorRef ? { approvalActorRef: options.approvalActorRef } : {}),\n ...(options.authorize !== undefined ? { authorize: options.authorize } : {}),\n ...(options.devTools !== undefined ? { devTools: options.devTools } : {}),\n }),\n ...(options.routes === false\n ? []\n : [\n RouterModule.register([\n { path: basePath, module: AgentDashboardModule },\n { path: apiBasePath, module: AgentApiModule },\n ]),\n ]),\n ],\n controllers:\n options.routes === false\n ? []\n : withControllerDecorators([AgentUiController], consoleDecorators(options.decorators)),\n providers: [\n { provide: DASHBOARD_BASE_PATH, useValue: basePath },\n { provide: DASHBOARD_API_PATH, useValue: apiBasePath },\n ],\n // Re-export the API module so its DashboardService reaches importers (e.g. the host's own\n // controllers), and the paths a self-mounted `AgentUiController` subclass injects.\n exports: [AgentApiModule, DASHBOARD_BASE_PATH, DASHBOARD_API_PATH],\n };\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { basename, extname, join, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { CanAnyone } from '@dudousxd/nestjs-agent-core';\nimport {\n Controller,\n Get,\n Header,\n Inject,\n NotFoundException,\n Param,\n Req,\n Res,\n StreamableFile,\n} from '@nestjs/common';\nimport { ConsolePage } from './console-authorize.guard.js';\nimport { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH } from './tokens.js';\n\n/**\n * The slice of the express request the Inertia bounce below reads — structural, so the package\n * needs no express dependency (the API controller similarly takes `@Req() req: unknown`).\n */\ninterface UiPageRequest {\n headers: Record<string, string | string[] | undefined>;\n /** Path + query as received (express `originalUrl`) — what the full-page visit must reload. */\n originalUrl: string;\n}\n\n/** The slice of the express response the Inertia bounce writes (passthrough mode — Nest still sends). */\ninterface UiPageResponse {\n status(code: number): unknown;\n setHeader(name: string, value: string): unknown;\n}\n\n/** The base the SPA bundle was built with (Vite `base`); rewritten to the configured base at serve time. */\nconst BUILD_BASE = '/ai-gateway';\n\n/** dist/server/agent-ui.controller.js -> ../spa (the Vite build output). */\nfunction spaDir(): string {\n return fileURLToPath(new URL('../spa', import.meta.url));\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.json': 'application/json; charset=utf-8',\n '.woff2': 'font/woff2',\n '.ico': 'image/x-icon',\n};\n\n/**\n * Serves the bundled AI-gateway console SPA at the configured base (+ hashed assets at\n * `<base>/assets`). The path comes from `RouterModule` (set by\n * {@link AgentDashboardModule.forRoot}({ basePath })), so the controller routes are relative.\n *\n * Who reaches it: `authorize` on `AgentDashboardModule.forRoot` (checked by `ConsoleAuthorizeGuard`,\n * after the app's `decorators`). Mounted by the app itself (`routes: false`), apply\n * `UseConsoleAuthorize()` to your subclass.\n */\n@CanAnyone()\n@Controller()\nexport class AgentUiController {\n private readonly dir = spaDir();\n\n constructor(\n @Inject(DASHBOARD_BASE_PATH) private readonly basePath: string,\n @Inject(DASHBOARD_API_PATH) private readonly apiBasePath: string,\n ) {}\n\n // index.html references hash-named bundles, so it MUST NOT be cached (stale bundle = the classic\n // \"stuck loading after a deploy\"). The hashed assets below are immutable.\n @Get()\n @ConsolePage()\n @Header('Content-Type', 'text/html; charset=utf-8')\n @Header('Cache-Control', 'no-store, must-revalidate')\n index(@Req() req: UiPageRequest, @Res({ passthrough: true }) res: UiPageResponse): string {\n // An Inertia <Link> in the host app visits this page route as an XHR expecting an Inertia JSON\n // page object; serving the SPA's HTML instead lands it in the Inertia client's `srcdoc` error\n // modal, where the page's relative asset URLs die on CORS (origin null). The protocol's own\n // escape hatch for \"this URL is not an Inertia page\" is a 409 Conflict carrying\n // `X-Inertia-Location`: the client responds with a full `window.location` visit, which renders\n // the console normally. Only HTML page routes need this — asset binaries are never fetched with\n // the header.\n if (req.headers['x-inertia'] !== undefined) {\n res.status(409);\n res.setHeader('X-Inertia-Location', req.originalUrl);\n return '';\n }\n const indexPath = join(this.dir, 'index.html');\n if (!existsSync(indexPath)) {\n throw new NotFoundException('Dashboard is not built. Run the package build.');\n }\n // The bundle was built with Vite base `/ai-gateway/`; rewrite asset URLs to the configured base\n // so the SPA loads from `<base>/assets` wherever it's mounted, and tell the client its API base.\n const html = readFileSync(indexPath, 'utf8').replaceAll(\n `=\"${BUILD_BASE}/`,\n `=\"${this.basePath}/`,\n );\n // __AGENT_BASE__ = where assets load; __AGENT_API__ = where the SPA fetches the JSON API.\n const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script>`;\n return html.includes('</head>') ? html.replace('</head>', `${inject}</head>`) : inject + html;\n }\n\n @Get('assets/:file')\n @Header('Cache-Control', 'public, max-age=31536000, immutable')\n asset(@Param('file') file: string): StreamableFile {\n const safe = basename(file);\n if (safe !== file) throw new NotFoundException();\n const root = resolve(this.dir, 'assets');\n const assetPath = resolve(root, safe);\n if (!assetPath.startsWith(root + sep) || !existsSync(assetPath)) {\n throw new NotFoundException();\n }\n const type = CONTENT_TYPES[extname(safe)] ?? 'application/octet-stream';\n return new StreamableFile(readFileSync(assetPath), { type });\n }\n}\n","import {\n type ArgumentsHost,\n type CanActivate,\n Catch,\n type ExceptionFilter,\n type ExecutionContext,\n HttpException,\n Inject,\n Injectable,\n type OnModuleInit,\n SetMetadata,\n UseFilters,\n UseGuards,\n applyDecorators,\n} from '@nestjs/common';\nimport { HttpAdapterHost, ModuleRef } from '@nestjs/core';\nimport {\n type ConsoleAuthorize,\n type ConsoleDecide,\n type ConsoleDecision,\n resolveConsoleAuthorize,\n} from './console-authorize.js';\nimport { DASHBOARD_CONSOLE_OPTIONS } from './tokens.js';\n\n/** What {@link DashboardConsoleAccess} is built from (bound per `forRoot`). */\nexport interface DashboardConsoleOptions {\n authorize?: ConsoleAuthorize;\n}\n\n/** Holds the console's resolved `authorize` check for one registration. */\n@Injectable()\nexport class DashboardConsoleAccess implements OnModuleInit {\n #decide: ConsoleDecide | undefined;\n\n constructor(\n @Inject(DASHBOARD_CONSOLE_OPTIONS) private readonly options: DashboardConsoleOptions,\n private readonly moduleRef: ModuleRef,\n ) {}\n\n #resolving: Promise<ConsoleDecide> | undefined;\n\n /** Resolves `authorize` at boot: a configuration mistake fails the boot, and the warning logs once. */\n async onModuleInit(): Promise<void> {\n this.#resolving ??= resolveConsoleAuthorize(this.options.authorize, this.moduleRef);\n this.#decide = await this.#resolving;\n }\n\n /** The decision for one request. */\n async check(context: ExecutionContext): Promise<ConsoleDecision> {\n if (this.#decide === undefined) await this.onModuleInit();\n return (this.#decide as ConsoleDecide)(context);\n }\n}\n\n/** Marks a controller or handler as serving pages: a denied browser navigation gets HTML or a 302. */\nexport const CONSOLE_PAGE_METADATA = '@dudousxd/nestjs-agent-dashboard:console-page';\n\n/**\n * On a controller or a handler that serves pages: a denied `GET` that accepts HTML gets a page (or a\n * 302 for `redirectTo`), not JSON.\n */\nexport const ConsolePage = (): ClassDecorator & MethodDecorator =>\n SetMetadata(CONSOLE_PAGE_METADATA, true);\n\n/** Thrown by {@link ConsoleAuthorizeGuard}; its body is the JSON answer even without the filter. */\nexport class ConsoleAccessDenied extends HttpException {\n constructor(\n readonly decision: Exclude<ConsoleDecision, { allowed: true }>,\n readonly page: boolean,\n ) {\n super(\n decision.reason === 'forbidden'\n ? { error: 'forbidden' }\n : {\n error: 'unauthenticated',\n ...(decision.redirectTo !== undefined ? { redirectTo: decision.redirectTo } : {}),\n },\n decision.reason === 'forbidden' ? 403 : 401,\n );\n }\n}\n\ninterface HttpRequestLike {\n method?: string;\n headers?: Record<string, string | string[] | undefined>;\n}\n\nfunction isPageNavigation(context: ExecutionContext): boolean {\n if (context.getType() !== 'http') return false;\n const page =\n Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getHandler()) === true ||\n Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) === true;\n if (!page) return false;\n const req = context.switchToHttp().getRequest<HttpRequestLike>();\n if ((req.method ?? 'GET').toUpperCase() !== 'GET') return false;\n const accept = req.headers?.accept;\n const value = Array.isArray(accept) ? accept.join(',') : (accept ?? '');\n return value.includes('text/html');\n}\n\n/**\n * Runs the console's `authorize` on every route of a controller it guards. Applied by the module\n * after the app's `decorators` (so authentication guards have already set `request.user`); a\n * self-mounted controller (`routes: false`) applies it with {@link UseConsoleAuthorize}.\n */\n@Injectable()\nexport class ConsoleAuthorizeGuard implements CanActivate {\n constructor(private readonly access: DashboardConsoleAccess) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const decision = await this.access.check(context);\n if (decision.allowed) return true;\n throw new ConsoleAccessDenied(decision, isPageNavigation(context));\n }\n}\n\n/** The minimal page a denied browser navigation gets. */\nfunction deniedPage(title: string, message: string): string {\n const body =\n 'font-family:system-ui,sans-serif;display:grid;place-items:center;min-height:100vh;margin:0';\n return `<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>${title}</title></head><body style=\"${body}\"><main style=\"text-align:center\"><h1>${title}</h1><p>${message}</p></main></body></html>`;\n}\n\nconst FORBIDDEN_PAGE = deniedPage('Access denied', 'You do not have access to this console.');\nconst UNAUTHENTICATED_PAGE = deniedPage('Sign in required', 'Sign in to open this console.');\n\n/** Writes a console denial: JSON for API calls; a 302 or an HTML page (403 / 401) for page navigations. */\n@Catch(ConsoleAccessDenied)\n@Injectable()\nexport class ConsoleAuthorizeFilter implements ExceptionFilter<ConsoleAccessDenied> {\n constructor(private readonly adapterHost: HttpAdapterHost) {}\n\n catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void {\n const adapter = this.adapterHost.httpAdapter;\n const res = host.switchToHttp().getResponse<unknown>();\n const { decision, page } = exception;\n if (page && decision.reason === 'unauthenticated' && decision.redirectTo !== undefined) {\n adapter.redirect(res, 302, decision.redirectTo);\n return;\n }\n if (page) {\n const forbidden = decision.reason === 'forbidden';\n adapter.setHeader(res, 'Content-Type', 'text/html; charset=utf-8');\n adapter.reply(res, forbidden ? FORBIDDEN_PAGE : UNAUTHENTICATED_PAGE, forbidden ? 403 : 401);\n return;\n }\n adapter.reply(res, exception.getResponse(), exception.getStatus());\n }\n}\n\n/**\n * The console's access check on a controller of your own (with `routes: false`): the\n * {@link ConsoleAuthorizeGuard} plus the filter that writes its denials. Write it ABOVE your own\n * authentication decorators: class decorators apply bottom-up, so it then runs after them and\n * `request.user` is set.\n */\nexport function UseConsoleAuthorize(): ClassDecorator & MethodDecorator {\n return applyDecorators(UseGuards(ConsoleAuthorizeGuard), UseFilters(ConsoleAuthorizeFilter));\n}\n","import { createHash, timingSafeEqual } from 'node:crypto';\nimport { type ExecutionContext, Logger, type Type } from '@nestjs/common';\nimport { ModuleRef } from '@nestjs/core';\n\n/**\n * What an `authorize` check answers. An allow may name who it let in (`caller`, for example\n * `'user:42'`): the dev MCP endpoint logs it as the caller of a developer tool. Never put a secret\n * in it.\n */\nexport type ConsoleAuthorizeResult =\n | boolean\n | { allowed: true; caller?: string }\n | { allowed: false; reason?: 'unauthenticated' | 'forbidden' }\n | { redirectTo: string };\n\n/** Class form: an @Injectable() provider (constructor injection works). */\nexport interface ConsoleAuthorizer {\n authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;\n}\nexport type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;\n\n/** @nestjs/authorization policy check: policy[ability](user). */\nexport interface ConsolePolicyCheck {\n policy: Type<unknown>;\n ability: string;\n}\n\n/** Who reaches the console. */\nexport type ConsoleAuthorize =\n | ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>) // inline\n | ConsoleAuthorizerClass // class\n | (() => Promise<{ default: ConsoleAuthorizerClass }>) // lazy import\n | ConsolePolicyCheck // official policy\n | string; // registered gate name\n\n/** A normalized `authorize` answer. */\nexport type ConsoleDecision =\n | { allowed: true; caller?: string }\n | { allowed: false; reason: 'unauthenticated' | 'forbidden'; redirectTo?: string };\n\n/** What a resolved `authorize` is: called once per request, never throws. */\nexport type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;\n\n/**\n * The provider that resolves gate names (`authorize: 'console.agent'`), bound globally by\n * `@dudousxd/nestjs-authz`'s `AuthzModule`. A registry symbol, so it is found without depending on\n * that package.\n */\nexport const CONSOLE_GATE = Symbol.for('@dudousxd/aviary:console-gate');\nexport interface ConsoleGate {\n allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;\n}\n\nconst PACKAGE = '@dudousxd/nestjs-agent-dashboard';\nconst MODULE = 'AgentDashboardModule';\nconst logger = new Logger(MODULE);\n\n/** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */\nexport const 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().`;\n\n// Fresh objects on every call: a decision is never a shared, mutable singleton.\nconst allow = (caller?: string): ConsoleDecision =>\n caller !== undefined && caller !== '' ? { allowed: true, caller } : { allowed: true };\nconst forbidden = (): ConsoleDecision => ({ allowed: false, reason: 'forbidden' });\nconst unauthenticated = (): ConsoleDecision => ({ allowed: false, reason: 'unauthenticated' });\n\nfunction isProduction(): boolean {\n return (process.env.NODE_ENV ?? '').trim().toLowerCase() === 'production';\n}\n\n/** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */\nexport function normalizeConsoleResult(result: unknown): ConsoleDecision {\n if (result === true) return allow();\n if (result === null || typeof result !== 'object') return forbidden();\n const value = result as {\n allowed?: unknown;\n reason?: unknown;\n redirectTo?: unknown;\n caller?: unknown;\n };\n if (typeof value.redirectTo === 'string' && value.redirectTo !== '' && value.allowed !== true) {\n return { allowed: false, reason: 'unauthenticated', redirectTo: value.redirectTo };\n }\n if (value.allowed === true) {\n return allow(typeof value.caller === 'string' ? value.caller : undefined);\n }\n return value.reason === 'unauthenticated' ? unauthenticated() : forbidden();\n}\n\n/**\n * A class with `authorize()`: on the prototype, or (an instance-property arrow function,\n * `authorize = (ctx) => ...`) only on instances, so any `class`.\n */\nfunction isAuthorizerClass(value: unknown): value is ConsoleAuthorizerClass {\n if (typeof value !== 'function') return false;\n if (\n typeof (value as { prototype?: { authorize?: unknown } }).prototype?.authorize === 'function'\n ) {\n return true;\n }\n return /^class[\\s{]/.test(Function.prototype.toString.call(value));\n}\n\nfunction isPolicyCheck(value: unknown): value is ConsolePolicyCheck {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as ConsolePolicyCheck).policy === 'function' &&\n typeof (value as ConsolePolicyCheck).ability === 'string'\n );\n}\n\nfunction lazyModuleClass(value: unknown): ConsoleAuthorizerClass | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const candidate = (value as { default?: unknown }).default;\n return isAuthorizerClass(candidate) ? candidate : undefined;\n}\n\n/** A provider registered anywhere (`strict: false`), or `undefined`. */\nfunction tryGet<T>(moduleRef: ModuleRef, token: unknown): T | undefined {\n try {\n return (moduleRef.get(token as never, { strict: false }) as T | undefined) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/** One instance per class per registration: a registered provider, else created with DI. */\nfunction classInstances(moduleRef: ModuleRef) {\n const instances = new Map<ConsoleAuthorizerClass, Promise<ConsoleAuthorizer>>();\n return (Class: ConsoleAuthorizerClass): Promise<ConsoleAuthorizer> => {\n let instance = instances.get(Class);\n if (instance === undefined) {\n const registered = tryGet<ConsoleAuthorizer>(moduleRef, Class);\n instance = registered !== undefined ? Promise.resolve(registered) : moduleRef.create(Class);\n // A failed create is retried on the next request, not cached as a permanent denial.\n instance.catch(() => instances.delete(Class));\n instances.set(Class, instance);\n }\n return instance;\n };\n}\n\n/** `null`, `undefined` and `false` mean \"nobody signed in\", like `@nestjs/authorization`'s guard. */\nfunction hasUser(user: unknown): boolean {\n return user !== null && user !== undefined && user !== false;\n}\n\n/** `{ policy, ability }`: `@nestjs/authorization` (an optional peer) imported lazily, checked at boot. */\nasync function policyDecider(\n check: ConsolePolicyCheck,\n moduleRef: ModuleRef,\n): Promise<(context: ExecutionContext) => Promise<ConsoleDecision>> {\n let mod: typeof import('@nestjs/authorization');\n try {\n mod = await import('@nestjs/authorization');\n } catch {\n throw new Error(\n `${MODULE}: \\`authorize: { policy, ability }\\` needs @nestjs/authorization installed and AuthorizationModule imported; or pass a function, a class, or a gate name.`,\n );\n }\n const service = tryGet<{\n can(policy: Type<unknown>, ability: string, user: unknown): Promise<boolean>;\n }>(moduleRef, mod.AuthorizationService);\n if (service === undefined) {\n throw new Error(\n `${MODULE}: \\`authorize: { policy, ability }\\` needs AuthorizationModule.forRoot() from @nestjs/authorization imported in the app.`,\n );\n }\n const options = tryGet<{ getUser?: (context: ExecutionContext) => unknown }>(\n moduleRef,\n mod.AUTHORIZATION_MODULE_OPTIONS,\n );\n // The user the official `@Can()` sees: the module's `getUser` when one is configured, else the\n // package default (`request.user`). `undefined`, `null` and `false` all mean a guest.\n const getUser = (options?.getUser ?? mod.defaultGetUser) as (\n context: ExecutionContext,\n ) => unknown;\n return async (context) => {\n const user = await getUser(context);\n const signedIn = hasUser(user);\n if (await service.can(check.policy, check.ability, signedIn ? user : null)) return allow();\n return signedIn ? forbidden() : unauthenticated();\n };\n}\n\n/**\n * Resolves `authorize` into one function the guard calls per request. Configuration mistakes (a\n * gate name without nestjs-authz, a policy without `@nestjs/authorization`) throw here, so call it\n * at boot. The returned function never throws: an error is a `forbidden`.\n *\n * `authorize` omitted: open outside production, `forbidden` for every request in production (read\n * when this runs), and one warning logged (unless `warnIfMissing: false`).\n */\nexport async function resolveConsoleAuthorize(\n authorize: ConsoleAuthorize | undefined,\n moduleRef: ModuleRef,\n options: { logger?: Logger; warnIfMissing?: boolean } = {},\n): Promise<ConsoleDecide> {\n const log = options.logger ?? logger;\n if (authorize === undefined) {\n if (options.warnIfMissing !== false) log.warn(CONSOLE_AUTHORIZE_MISSING_WARNING);\n const production = isProduction();\n return async () => (production ? forbidden() : allow('dev-open'));\n }\n\n const instanceOf = classInstances(moduleRef);\n let decide: (context: ExecutionContext) => Promise<unknown>;\n if (typeof authorize === 'string') {\n const name = authorize;\n const gate = tryGet<ConsoleGate>(moduleRef, CONSOLE_GATE);\n if (gate === undefined) {\n throw new Error(\n `${MODULE}: \\`authorize: '${name}'\\` needs @dudousxd/nestjs-authz's AuthzModule (it resolves gate names); or pass a function, a class, or { policy, ability }.`,\n );\n }\n decide = (context) => gate.allows(name, context);\n } else if (isPolicyCheck(authorize)) {\n decide = await policyDecider(authorize, moduleRef);\n } else if (isAuthorizerClass(authorize)) {\n const Class = authorize;\n decide = async (context) => (await instanceOf(Class)).authorize(context);\n } else if (typeof authorize === 'function') {\n const fn = authorize as (context: ExecutionContext) => unknown;\n let lazyClass: ConsoleAuthorizerClass | undefined;\n decide = async (context) => {\n if (lazyClass !== undefined) return (await instanceOf(lazyClass)).authorize(context);\n const result = await fn(context);\n const Class = lazyModuleClass(result);\n if (Class === undefined) return result;\n // A lazy import: remember the class, so the import runs once per registration.\n lazyClass = Class;\n return (await instanceOf(Class)).authorize(context);\n };\n } else {\n throw new Error(\n `${MODULE}: \\`authorize\\` must be a function, a class with authorize(), a lazy import, { policy, ability } or a gate name.`,\n );\n }\n\n return async (context) => {\n try {\n return normalizeConsoleResult(await decide(context));\n } catch (error) {\n log.warn(\n `console \\`authorize\\` threw, denying the request: ${error instanceof Error ? error.message : String(error)}`,\n );\n return forbidden();\n }\n };\n}\n\nfunction sha256(value: string): Buffer {\n return createHash('sha256').update(value).digest();\n}\n\n/**\n * `authorize` by a bearer token: allows when `Authorization: Bearer <token>` matches (compared in\n * constant time), else `unauthenticated`. Header only. An empty token throws here, at config time.\n * The allow names its caller `token:<first 12 hex of sha256(token)>`, never the token.\n */\nexport function authorizeByToken(options: { token: string | undefined }): (\n context: ExecutionContext,\n) => ConsoleAuthorizeResult {\n const token = options.token;\n if (typeof token !== 'string' || token === '') {\n throw new Error('authorizeByToken: `token` is empty; set it (e.g. from an env var).');\n }\n const expected = sha256(token);\n const caller = `token:${expected.toString('hex').slice(0, 12)}`;\n return (context) => {\n const req = context\n .switchToHttp()\n .getRequest<{ headers?: Record<string, string | string[] | undefined> }>();\n const header = req?.headers?.authorization;\n const value = Array.isArray(header) ? header[0] : header;\n const match = typeof value === 'string' ? /^Bearer\\s+(.+)$/i.exec(value.trim()) : null;\n if (match?.[1] !== undefined && timingSafeEqual(sha256(match[1]), expected)) {\n return { allowed: true, caller };\n }\n return { allowed: false, reason: 'unauthenticated' };\n };\n}\n\n/**\n * One `authorize` from several: allowed when any form allows (checked in order, stopping at the\n * first that does, whose `caller` it keeps); otherwise `unauthenticated` when any form said so (keeping the first\n * `redirectTo`), else `forbidden`. Each form is any `authorize` value: a function, a class, a lazy\n * import, `{ policy, ability }` or a gate name.\n *\n * ```ts\n * authorize: anyAuthorize(authorizeByToken({ token: process.env.CONSOLE_TOKEN }), AdminSessionAuthorizer)\n * ```\n */\nexport function anyAuthorize(...forms: ConsoleAuthorize[]): ConsoleAuthorizerClass {\n if (forms.length === 0) throw new Error('anyAuthorize: pass at least one authorize form.');\n class AnyConsoleAuthorizer implements ConsoleAuthorizer {\n #decides: Promise<ConsoleDecide[]> | undefined;\n\n constructor(private readonly moduleRef: ModuleRef) {}\n\n async authorize(context: ExecutionContext): Promise<ConsoleAuthorizeResult> {\n this.#decides ??= Promise.all(\n forms.map((form) =>\n resolveConsoleAuthorize(form, this.moduleRef, { warnIfMissing: false }),\n ),\n );\n // A failed resolution is retried on the next request, not cached as a permanent denial.\n this.#decides.catch(() => {\n this.#decides = undefined;\n });\n let unauthenticated: ConsoleDecision | undefined;\n for (const decide of await this.#decides) {\n const decision = await decide(context);\n if (decision.allowed) return decision;\n if (decision.reason === 'unauthenticated') unauthenticated ??= decision;\n }\n if (unauthenticated === undefined) return { allowed: false, reason: 'forbidden' };\n return 'redirectTo' in unauthenticated && unauthenticated.redirectTo !== undefined\n ? { redirectTo: unauthenticated.redirectTo }\n : { allowed: false, reason: 'unauthenticated' };\n }\n }\n // `ModuleRef` by explicit metadata: the class is created with DI whatever the build emits.\n Reflect.defineMetadata('design:paramtypes', [ModuleRef], AnyConsoleAuthorizer);\n return AnyConsoleAuthorizer;\n}\n","import type { ExecutionContext } from '@nestjs/common';\nimport {\n Inject,\n Injectable,\n Logger,\n NotFoundException,\n type OnModuleDestroy,\n type OnModuleInit,\n} from '@nestjs/common';\nimport { DashboardConsoleAccess } from './console-authorize.guard.js';\nimport type { ConsoleDecision } from './console-authorize.js';\nimport { DashboardService } from './dashboard.service.js';\nimport { DASHBOARD_DEV_TOOLS } from './tokens.js';\n\n// ── the dev-tools contract (v1), re-declared: this package never imports the MCP server ──────\n\n/** The process-wide slot `@dudousxd/nestjs-agent-mcp-server` serves on its dev MCP endpoint. */\nconst DEV_TOOLS_SLOT = Symbol.for('@dudousxd/aviary:dev-tools');\n\ninterface DevToolInputProperty {\n type: 'string' | 'number' | 'integer' | 'boolean' | 'array';\n description?: string;\n enum?: readonly string[];\n items?: { type: 'string' };\n minimum?: number;\n maximum?: number;\n}\n\ninterface DevToolInputSchema {\n type: 'object';\n properties: Record<string, DevToolInputProperty>;\n required?: string[];\n}\n\n/** One developer tool, as the dev-tools slot takes it. */\nexport interface AgentDevTool {\n name: string;\n kind: 'read' | 'action';\n description: string;\n input: DevToolInputSchema;\n run(input: Record<string, unknown>): Promise<unknown>;\n}\n\ninterface DevToolsLib {\n lib: string;\n title: string;\n authorize(context: ExecutionContext): Promise<ConsoleDecision>;\n tools: AgentDevTool[];\n}\n\ninterface DevToolsSlot {\n readonly version: 1;\n readonly libs: DevToolsLib[];\n register(lib: DevToolsLib): () => void;\n}\n\n/** The slot, created when absent (whoever loads first creates it). */\nfunction devToolsSlot(): Partial<DevToolsSlot> {\n const holder = globalThis as Record<symbol, unknown>;\n const existing = holder[DEV_TOOLS_SLOT] as Partial<DevToolsSlot> | undefined;\n if (existing !== undefined) return existing;\n const libs: DevToolsLib[] = [];\n const slot: DevToolsSlot = {\n version: 1,\n libs,\n register(lib) {\n if (libs.some((entry) => entry.lib === lib.lib)) {\n throw new Error(`dev tools: the lib \"${lib.lib}\" is already registered`);\n }\n libs.push(lib);\n return () => {\n const index = libs.indexOf(lib);\n if (index !== -1) libs.splice(index, 1);\n };\n },\n };\n holder[DEV_TOOLS_SLOT] = slot;\n return slot;\n}\n\n// ── the agent's own tools ──────────────────────────────────────────────────────\n\nconst DEFAULT_LIMIT = 20;\nconst MAX_LIMIT = 100;\n/** Messages `agent_get_run` returns with `includeMessages: true`, each cut to 2 KB. */\nconst MESSAGE_LIMIT = 50;\nconst MESSAGE_BYTES = 2 * 1024;\nconst DAY_MS = 86_400_000;\nconst ISO_DAY = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst day: DevToolInputProperty = { type: 'string', description: 'UTC day, YYYY-MM-DD.' };\nconst page: DevToolInputProperty = {\n type: 'integer',\n description: 'Page, from 1. Pass the `next` of the previous page.',\n minimum: 1,\n};\nconst limit: DevToolInputProperty = {\n type: 'integer',\n description: `Rows per page (default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}).`,\n minimum: 1,\n maximum: MAX_LIMIT,\n};\n\nconst str = (value: unknown) => (typeof value === 'string' && value !== '' ? value : undefined);\n\nfunction dayOf(input: Record<string, unknown>, key: string): string | undefined {\n const value = str(input[key]);\n if (value !== undefined && !ISO_DAY.test(value)) throw new Error(`\"${key}\" must be YYYY-MM-DD`);\n return value;\n}\n\n/** `from`/`to`, defaulting to the last 30 days like the console. */\nfunction rangeOf(input: Record<string, unknown>) {\n const utcDay = (daysAgo: number) =>\n new Date(Date.now() - daysAgo * DAY_MS).toISOString().slice(0, 10);\n return { fromDay: dayOf(input, 'from') ?? utcDay(29), toDay: dayOf(input, 'to') ?? utcDay(0) };\n}\n\nfunction pageOf(input: Record<string, unknown>) {\n return {\n page: typeof input.page === 'number' ? input.page : 1,\n pageSize: typeof input.limit === 'number' ? input.limit : DEFAULT_LIMIT,\n };\n}\n\n/** Only the filters that were given (`exactOptionalPropertyTypes`). */\nfunction where<T extends Record<string, string | undefined>>(fields: T) {\n const set = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined));\n return Object.keys(set).length > 0 ? { where: set as { [K in keyof T]?: string } } : {};\n}\n\n/** A console page as a dev-tools list: `{ items, next, total }`. */\nfunction listOf<Row>(result: { rows: Row[]; total: number; page: number; pageSize: number }) {\n return {\n items: result.rows,\n next: result.page * result.pageSize < result.total ? result.page + 1 : null,\n total: result.total,\n };\n}\n\nfunction cut(text: string, maxBytes: number): string {\n const size = Buffer.byteLength(text, 'utf8');\n if (size <= maxBytes) return text;\n const suffix = `…(truncated, ${size} bytes)`;\n const keep = Math.max(0, maxBytes - Buffer.byteLength(suffix, 'utf8'));\n let head = Buffer.from(text, 'utf8').subarray(0, keep).toString('utf8');\n if (head.endsWith('�')) head = head.slice(0, -1);\n return `${head}${suffix}`;\n}\n\n/**\n * The agent's developer tools (lib `agent`): the governance console's reads, through the same\n * {@link DashboardService} methods its API serves. No actions: approving a tool call over MCP would\n * bypass the human approval it exists for.\n */\nexport function agentDevTools(dashboard: DashboardService): AgentDevTool[] {\n return [\n {\n name: 'agent_list_runs',\n kind: 'read',\n description:\n '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.',\n input: {\n type: 'object',\n properties: {\n agent: { type: 'string', description: 'Agent name.' },\n status: { type: 'string', enum: ['running', 'completed', 'failed', 'cancelled'] },\n errorCode: { type: 'string' },\n threadId: { type: 'string' },\n from: day,\n to: day,\n page,\n limit,\n },\n },\n async run(input) {\n return listOf(\n await dashboard.runsPage({\n ...pageOf(input),\n ...where({\n agentName: str(input.agent),\n status: str(input.status),\n errorCode: str(input.errorCode),\n threadId: str(input.threadId),\n fromDay: dayOf(input, 'from'),\n toDay: dayOf(input, 'to'),\n }),\n }),\n );\n },\n },\n {\n name: 'agent_get_run',\n kind: 'read',\n description:\n \"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.\",\n input: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Run id.' },\n includeMessages: { type: 'boolean', description: 'Include messages (default false).' },\n },\n required: ['id'],\n },\n async run(input) {\n let detail: Awaited<ReturnType<DashboardService['runDetail']>>;\n try {\n detail = await dashboard.runDetail(String(input.id));\n } catch (error) {\n // The endpoint answers a throw as an MCP `isError` result.\n if (error instanceof NotFoundException)\n throw new Error(`run ${String(input.id)} not found`);\n throw error;\n }\n if (input.includeMessages !== true) return detail;\n const thread = await dashboard.threadDetail({\n threadId: detail.run.threadId,\n messageLimit: MESSAGE_LIMIT,\n runLimit: 1,\n });\n return {\n ...detail,\n messages: thread.messages.map((message) => ({\n ...message,\n content: cut(message.content, MESSAGE_BYTES),\n })),\n };\n },\n },\n {\n name: 'agent_get_spend',\n kind: 'read',\n description:\n '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.',\n input: {\n type: 'object',\n properties: {\n by: { type: 'string', enum: ['model', 'actor', 'feature'] },\n from: day,\n to: day,\n },\n },\n async run(input) {\n const spend = await dashboard.spend(rangeOf(input));\n switch (input.by) {\n case 'model':\n return { items: spend.byModel };\n case 'actor':\n return { items: spend.byActor };\n case 'feature':\n return { items: spend.byFeature ?? [] };\n default:\n return spend;\n }\n },\n },\n {\n name: 'agent_get_tool_stats',\n kind: 'read',\n description:\n 'Per tool: calls, failures, rejections and latency, over UTC days from/to (default the last 30 days).',\n input: { type: 'object', properties: { from: day, to: day } },\n async run(input) {\n return { items: await dashboard.toolStats(rangeOf(input)) };\n },\n },\n {\n name: 'agent_get_reliability',\n kind: 'read',\n description:\n 'Run metrics, by-agent and by-error breakdowns and the trend, over UTC days from/to (default the last 30 days).',\n input: { type: 'object', properties: { from: day, to: day } },\n async run(input) {\n return dashboard.reliability(rangeOf(input));\n },\n },\n {\n name: 'agent_list_pending_approvals',\n kind: 'read',\n description:\n 'Tool calls waiting for a human approval, oldest first. Read-only: approve them in the app. Returns { items, next, total }.',\n input: {\n type: 'object',\n properties: {\n toolName: { type: 'string' },\n agent: { type: 'string', description: 'Agent name.' },\n actor: { type: 'string', description: 'Actor ref of the thread owner.' },\n threadId: { type: 'string' },\n from: day,\n to: day,\n page,\n limit,\n },\n },\n async run(input) {\n return listOf(\n await dashboard.approvalsPage({\n ...pageOf(input),\n ...where({\n toolName: str(input.toolName),\n agentName: str(input.agent),\n actorRef: str(input.actor),\n threadId: str(input.threadId),\n fromDay: dayOf(input, 'from'),\n toDay: dayOf(input, 'to'),\n }),\n }),\n );\n },\n },\n {\n name: 'agent_list_recent_tool_calls',\n kind: 'read',\n description:\n 'Tool calls of every agent, newest first. Filters: toolName, status, threadId, from/to (UTC days). Returns { items, next, total }.',\n input: {\n type: 'object',\n properties: {\n toolName: { type: 'string' },\n status: { type: 'string' },\n threadId: { type: 'string' },\n from: day,\n to: day,\n page,\n limit,\n },\n },\n async run(input) {\n return listOf(\n await dashboard.toolCallsPage({\n ...pageOf(input),\n ...where({\n toolName: str(input.toolName),\n status: str(input.status),\n threadId: str(input.threadId),\n fromDay: dayOf(input, 'from'),\n toDay: dayOf(input, 'to'),\n }),\n }),\n );\n },\n },\n ];\n}\n\nconst logger = new Logger('AgentDashboardModule');\n\n/**\n * Registers the agent's tools into the dev-tools slot, gated by the console's own resolved\n * `authorize`, when the console is mounted (or `devTools: true`). Removed again on shutdown.\n */\n@Injectable()\nexport class AgentDevToolsRegistration implements OnModuleInit, OnModuleDestroy {\n #unregister: (() => void) | undefined;\n\n constructor(\n @Inject(DASHBOARD_DEV_TOOLS) private readonly enabled: boolean,\n private readonly dashboard: DashboardService,\n private readonly access: DashboardConsoleAccess,\n ) {}\n\n onModuleInit(): void {\n if (!this.enabled) return;\n const slot = devToolsSlot();\n if (slot.version !== 1 || typeof slot.register !== 'function') return;\n try {\n this.#unregister = slot.register({\n lib: 'agent',\n title: 'Agent governance',\n authorize: (context) => this.access.check(context),\n tools: agentDevTools(this.dashboard),\n });\n } catch (error) {\n // Another application in this process registered the agent's tools: keep the console up.\n logger.warn(\n `agent dev tools not registered: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n onModuleDestroy(): void {\n this.#unregister?.();\n this.#unregister = undefined;\n }\n}\n"],"mappings":"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","names":["CanAnyone","Body","Controller","Get","HttpCode","Inject","Optional","Param","Post","Query","Req","Sse","subscribe","unsubscribe","channelName","Inject","Injectable","NotFoundException","NotImplementedException","Optional","Observable","BadRequestException","parseApprovalDecision","body","BadRequestException","approved","reason","undefined","BadRequestException","parsePriceInput","body","BadRequestException","modelId","inputPricePer1m","outputPricePer1m","cacheWritePricePer1m","cacheReadPricePer1m","trim","length","isFiniteNonNegative","undefined","value","Number","isFinite","AGENT_GOVERNANCE_QUERIES","Symbol","for","AGENT_ACTOR_DIRECTORY","AGENT_PRICING_STORE","AGENT_APPROVAL_PORT","AGENT_ACTOR_RESOLVER","DASHBOARD_BASE_PATH","DASHBOARD_API_PATH","DASHBOARD_APPROVAL_ACTOR_REF","DASHBOARD_CONSOLE_OPTIONS","DASHBOARD_DEV_TOOLS","PRICING_STORE_UNBOUND_MESSAGE","APPROVAL_PORT_UNBOUND_MESSAGE","GENUI_STATS_UNSUPPORTED_MESSAGE","AGENT_EVENTS","isAgentEnvelope","message","event","DashboardService","queries","actorDirectory","pricingStore","approvalPort","spend","range","byModel","byActorRaw","trend","byFeature","Promise","all","spendByModel","spendByActor","usageTrend","spendByFeature","byActor","withActorLabels","undefined","topThreads","limit","rows","spendByThread","reliability","metrics","byAgent","errors","runMetrics","runsByAgent","runErrors","runTrend","recentRuns","recentToolCalls","toolCallsPage","query","runsPage","pendingApprovals","approvalsPage","runDetail","runId","detail","NotFoundException","thread","actorLabel","threadDetail","threadId","toolStats","genuiStats","NotImplementedException","decideApproval","toolCallId","body","executedByRef","decision","parseApprovalDecision","opts","approved","approve","reject","reason","recentThreads","threadsPage","page","length","map","row","refs","Set","actorRef","resolved","resolveDisplay","listPrices","listCurrentPrices","upsertPrice","upsertModelPrice","parsePriceInput","streamEvents","Observable","subscriber","bindings","name","channelName","handler","next","data","ts","Date","now","payload","subscribe","binding","unsubscribe","BadRequestException","ISO_DAY","parsePageNumber","value","parsed","undefined","Number","NaN","parseInt","isFinite","assertKnownFields","raw","allowed","field","Object","keys","includes","BadRequestException","assertDayFields","dayFields","test","DAY_FIELDS","TOOL_CALL_WHERE_FIELDS","parseToolCallWhere","length","toolName","toolType","status","threadId","fromDay","toDay","THREAD_WHERE_FIELDS","parseThreadWhere","actorRef","title","RUN_WHERE_FIELDS","parseRunWhere","agentName","errorCode","APPROVAL_WHERE_FIELDS","parseApprovalWhere","DAY_MS","ISO_DAY","utcDay","daysAgo","Date","now","toISOString","slice","dayOr","value","fallback","undefined","test","resolveRange","from","to","fromDay","toDay","parseLimit","parsed","Number","NaN","parseInt","isFinite","Math","max","min","AgentApiController","dashboard","approvalActorRef","actorResolver","spend","topThreads","limit","reliability","runs","recentRuns","runsPage","page","where","parsedWhere","parseRunWhere","parsePageNumber","pageSize","runDetail","runId","toolCalls","recentToolCalls","toolCallsPage","parseToolCallWhere","approvals","pendingApprovals","approvalsPage","parseApprovalWhere","decideApproval","toolCallId","body","req","deciderRef","resolve","id","tools","toolStats","genui","genuiStats","threads","recentThreads","threadsPage","parseThreadWhere","threadDetail","threadId","messages","messageLimit","runLimit","listPrices","upsertPrice","stream","streamEvents","normalizeDashboardPath","path","replace","unprefixed","path","replace","agentDashboardMountPaths","options","basePath","normalizeDashboardPath","apiBasePath","base","api","withControllerDecorators","Module","UseFilters","UseGuards","RouterModule","existsSync","readFileSync","basename","extname","join","resolve","sep","fileURLToPath","CanAnyone","Controller","Get","Header","Inject","NotFoundException","Param","Req","Res","StreamableFile","Catch","HttpException","Inject","Injectable","SetMetadata","UseFilters","UseGuards","applyDecorators","HttpAdapterHost","ModuleRef","createHash","timingSafeEqual","Logger","ModuleRef","CONSOLE_GATE","Symbol","for","PACKAGE","MODULE","logger","Logger","CONSOLE_AUTHORIZE_MISSING_WARNING","allow","caller","undefined","allowed","forbidden","reason","unauthenticated","isProduction","process","env","NODE_ENV","trim","toLowerCase","normalizeConsoleResult","result","value","redirectTo","isAuthorizerClass","prototype","authorize","test","Function","toString","call","isPolicyCheck","policy","ability","lazyModuleClass","candidate","default","tryGet","moduleRef","token","get","strict","classInstances","instances","Map","Class","instance","registered","Promise","resolve","create","catch","delete","set","hasUser","user","policyDecider","check","mod","Error","service","AuthorizationService","options","AUTHORIZATION_MODULE_OPTIONS","getUser","defaultGetUser","context","signedIn","can","resolveConsoleAuthorize","log","warnIfMissing","warn","production","instanceOf","decide","name","gate","allows","fn","lazyClass","error","message","String","sha256","createHash","update","digest","authorizeByToken","expected","slice","req","switchToHttp","getRequest","header","headers","authorization","Array","isArray","match","exec","timingSafeEqual","anyAuthorize","forms","length","AnyConsoleAuthorizer","all","map","form","decision","Reflect","defineMetadata","ModuleRef","DashboardConsoleAccess","options","moduleRef","onModuleInit","resolveConsoleAuthorize","authorize","check","context","undefined","CONSOLE_PAGE_METADATA","ConsolePage","SetMetadata","ConsoleAccessDenied","HttpException","decision","page","reason","error","redirectTo","isPageNavigation","getType","Reflect","getMetadata","getHandler","getClass","req","switchToHttp","getRequest","method","toUpperCase","accept","headers","value","Array","isArray","join","includes","ConsoleAuthorizeGuard","access","canActivate","allowed","deniedPage","title","message","body","FORBIDDEN_PAGE","UNAUTHENTICATED_PAGE","ConsoleAuthorizeFilter","adapterHost","catch","exception","host","adapter","httpAdapter","res","getResponse","redirect","forbidden","setHeader","reply","getStatus","UseConsoleAuthorize","applyDecorators","UseGuards","UseFilters","BUILD_BASE","spaDir","fileURLToPath","URL","url","CONTENT_TYPES","AgentUiController","dir","basePath","apiBasePath","index","req","res","headers","undefined","status","setHeader","originalUrl","indexPath","join","existsSync","NotFoundException","html","readFileSync","replaceAll","inject","includes","replace","asset","file","safe","basename","root","resolve","assetPath","startsWith","sep","type","extname","StreamableFile","passthrough","Inject","Injectable","Logger","NotFoundException","DEV_TOOLS_SLOT","Symbol","for","devToolsSlot","holder","globalThis","existing","undefined","libs","slot","version","register","lib","some","entry","Error","push","index","indexOf","splice","DEFAULT_LIMIT","MAX_LIMIT","MESSAGE_LIMIT","MESSAGE_BYTES","DAY_MS","ISO_DAY","day","type","description","page","minimum","limit","maximum","str","value","dayOf","input","key","test","rangeOf","utcDay","daysAgo","Date","now","toISOString","slice","fromDay","toDay","pageOf","pageSize","where","fields","set","Object","fromEntries","entries","filter","v","keys","length","listOf","result","items","rows","next","total","cut","text","maxBytes","size","Buffer","byteLength","suffix","keep","Math","max","head","from","subarray","toString","endsWith","agentDevTools","dashboard","name","kind","properties","agent","status","enum","errorCode","threadId","to","run","runsPage","agentName","id","includeMessages","required","detail","runDetail","String","error","NotFoundException","thread","threadDetail","messageLimit","runLimit","messages","map","message","content","by","spend","byModel","byActor","byFeature","toolStats","reliability","toolName","actor","approvalsPage","actorRef","toolCallsPage","logger","Logger","AgentDevToolsRegistration","enabled","access","onModuleInit","title","authorize","context","check","tools","warn","onModuleDestroy","normalize","path","normalizeDashboardPath","consoleDecorators","decorators","UseGuards","ConsoleAuthorizeGuard","UseFilters","ConsoleAuthorizeFilter","AgentApiModule","register","options","consoleOptions","authorize","undefined","module","imports","controllers","routes","withControllerDecorators","AgentApiController","providers","DashboardService","provide","DASHBOARD_APPROVAL_ACTOR_REF","useValue","approvalActorRef","DASHBOARD_CONSOLE_OPTIONS","DashboardConsoleAccess","DASHBOARD_DEV_TOOLS","devTools","AgentDevToolsRegistration","exports","AgentDashboardModule","forRoot","basePath","apiBasePath","RouterModule","AgentUiController","DASHBOARD_BASE_PATH","DASHBOARD_API_PATH"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/agent-api.controller.ts","../../src/server/dashboard.service.ts","../../src/server/parse-approval-decision.ts","../../src/server/parse-price-input.ts","../../src/server/tokens.ts","../../src/server/parse-page-query.ts","../../src/server/normalize-path.ts","../../src/server/agent-dashboard-mount-paths.ts","../../src/server/agent-dashboard.module.ts","../../src/server/agent-ui.controller.ts","../../src/server/console-authorize.guard.ts","../../src/server/console-authorize.ts","../../src/server/console-switcher.ts","../../src/server/dev-tools.ts"],"sourcesContent":["import { CanAnyone } from '@dudousxd/nestjs-agent-core';\nimport type {\n ActorResolver,\n CurrentModelPrice,\n GenuiStats,\n GovernancePage,\n PendingApprovalRow,\n RecentRunRow,\n ToolCallActivityRow,\n ToolStatRow,\n} from '@dudousxd/nestjs-agent-core';\nimport {\n Body,\n Controller,\n Get,\n HttpCode,\n Inject,\n Optional,\n Param,\n Post,\n Query,\n Req,\n Sse,\n} from '@nestjs/common';\nimport type { Observable } from 'rxjs';\nimport {\n DashboardService,\n type LiveAgentEvent,\n type ReliabilityOverview,\n type RunDetailWithLabel,\n type SpendOverview,\n type ThreadActivityRowWithLabel,\n type ThreadDetailWithLabel,\n type ThreadSpendRowWithLabel,\n} from './dashboard.service.js';\nimport {\n parseApprovalWhere,\n parsePageNumber,\n parseRunWhere,\n parseThreadWhere,\n parseToolCallWhere,\n} from './parse-page-query.js';\nimport { AGENT_ACTOR_RESOLVER, DASHBOARD_APPROVAL_ACTOR_REF } from './tokens.js';\n\nconst DAY_MS = 86_400_000;\nconst ISO_DAY = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** A `YYYY-MM-DD` UTC day string `daysAgo` days before now (0 = today). */\nfunction utcDay(daysAgo: number): string {\n return new Date(Date.now() - daysAgo * DAY_MS).toISOString().slice(0, 10);\n}\n\n/** Accept a client-supplied `YYYY-MM-DD`, or fall back to `fallback`; guards against junk input. */\nfunction dayOr(value: string | undefined, fallback: string): string {\n return value !== undefined && ISO_DAY.test(value) ? value : fallback;\n}\n\n/** Resolve the `from`/`to` query params into a validated range, defaulting to the last 30 days. */\nfunction resolveRange(\n from: string | undefined,\n to: string | undefined,\n): {\n fromDay: string;\n toDay: string;\n} {\n return { fromDay: dayOr(from, utcDay(29)), toDay: dayOr(to, utcDay(0)) };\n}\n\n/** Parse a `limit` query param, clamped to a sane window; falls back to `fallback` when absent/junk. */\nfunction parseLimit(value: string | undefined, fallback: number): number {\n const parsed = value === undefined ? Number.NaN : Number.parseInt(value, 10);\n if (!Number.isFinite(parsed)) return fallback;\n return Math.max(1, Math.min(200, parsed));\n}\n\n/**\n * JSON + SSE API consumed by the AI-gateway console SPA. Mounted at `apiBasePath` (set by\n * `RouterModule` in {@link AgentDashboardModule.forRoot}), so the controller routes are relative.\n *\n * Who reaches it: `authorize` on `AgentDashboardModule.forRoot` (checked by `ConsoleAuthorizeGuard`,\n * after the app's `decorators`). Mounted by the app itself (`routes: false`), apply\n * `UseConsoleAuthorize()` to your subclass.\n */\n@CanAnyone()\n@Controller()\nexport class AgentApiController {\n constructor(\n private readonly dashboard: DashboardService,\n // `useValue` even when the host omitted `approvalActorRef` — no `@Optional()` needed (the token\n // is always bound; see DASHBOARD_APPROVAL_ACTOR_REF's TSDoc).\n @Inject(DASHBOARD_APPROVAL_ACTOR_REF)\n private readonly approvalActorRef: ((req: unknown) => string | undefined) | undefined,\n // The app's identity seam, bound + exported by the (global) AgentModule — the DEFAULT decider\n // attribution when no `approvalActorRef` override is configured. Genuinely `@Optional()`: the\n // dashboard can be mounted without AgentModule (read-model only), where nothing binds it.\n @Optional()\n @Inject(AGENT_ACTOR_RESOLVER)\n private readonly actorResolver?: ActorResolver,\n ) {}\n\n /** `{ byModel, byActor, trend }` for a day range (defaults to the last 30 days). */\n @Get('spend')\n spend(@Query('from') from?: string, @Query('to') to?: string): Promise<SpendOverview> {\n return this.dashboard.spend(resolveRange(from, to));\n }\n\n /** Top threads by cost (default 10, max 200) for a day range (defaults to the last 30 days). */\n @Get('top-threads')\n topThreads(\n @Query('from') from?: string,\n @Query('to') to?: string,\n @Query('limit') limit?: string,\n ): Promise<ThreadSpendRowWithLabel[]> {\n return this.dashboard.topThreads(resolveRange(from, to), parseLimit(limit, 10));\n }\n\n /** `{ metrics, byAgent, errors, trend }` for a day range (defaults to the last 30 days). */\n @Get('reliability')\n reliability(\n @Query('from') from?: string,\n @Query('to') to?: string,\n ): Promise<ReliabilityOverview> {\n return this.dashboard.reliability(resolveRange(from, to));\n }\n\n /** Most recent runs (default 50, max 200) for the Reliability recent-runs table. */\n @Get('runs')\n runs(@Query('limit') limit?: string): Promise<RecentRunRow[]> {\n return this.dashboard.recentRuns(parseLimit(limit, 50));\n }\n\n /**\n * Paged, filterable runs for the Reliability recent-runs table. `page` (default 1), `limit`\n * (default 25, max 200), and `where[agentName]`/`where[status]`/`where[errorCode]`/\n * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.\n */\n @Get('runs-page')\n runsPage(\n @Query('page') page?: string,\n @Query('limit') limit?: string,\n @Query('where') where?: Record<string, string>,\n ): Promise<GovernancePage<RecentRunRow>> {\n const parsedWhere = parseRunWhere(where);\n return this.dashboard.runsPage({\n page: parsePageNumber(page),\n pageSize: parseLimit(limit, 25),\n ...(parsedWhere !== undefined ? { where: parsedWhere } : {}),\n });\n }\n\n /**\n * One run drill-down: the run, its owning thread's headline (actor-labeled) and its tool calls, in\n * ONE round trip. 404s on an unknown run id.\n *\n * Declared AFTER `runs-page` so the literal route wins over this parameterised one regardless of\n * how the router orders same-prefix paths.\n */\n @Get('runs/:runId')\n runDetail(@Param('runId') runId: string): Promise<RunDetailWithLabel> {\n return this.dashboard.runDetail(runId);\n }\n\n /** Most recent tool calls (default 50, max 200) for the activity feed. */\n @Get('tool-calls')\n toolCalls(@Query('limit') limit?: string): Promise<ToolCallActivityRow[]> {\n return this.dashboard.recentToolCalls(parseLimit(limit, 50));\n }\n\n /**\n * Paged, filterable tool calls for the Runs & tools activity table. `page` (default 1), `limit`\n * (default 25, max 200), and `where[toolName]`/`where[toolType]`/`where[status]`/`where[threadId]`/\n * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.\n */\n @Get('tool-calls-page')\n toolCallsPage(\n @Query('page') page?: string,\n @Query('limit') limit?: string,\n @Query('where') where?: Record<string, string>,\n ): Promise<GovernancePage<ToolCallActivityRow>> {\n const parsedWhere = parseToolCallWhere(where);\n return this.dashboard.toolCallsPage({\n page: parsePageNumber(page),\n pageSize: parseLimit(limit, 25),\n ...(parsedWhere !== undefined ? { where: parsedWhere } : {}),\n });\n }\n\n /**\n * Tool calls sitting `pending_approval` (default 50, max 200), oldest first — the approvals inbox.\n *\n * Capped with NO total, so a backlog past `limit` is invisible from the response alone. Kept\n * because the SPA still calls it; anything new should call `approvals-page` instead.\n */\n @Get('approvals')\n approvals(@Query('limit') limit?: string): Promise<PendingApprovalRow[]> {\n return this.dashboard.pendingApprovals(parseLimit(limit, 50));\n }\n\n /**\n * Paged, filterable approvals inbox, oldest first. `page` (default 1), `limit` (default 25, max\n * 200), and `where[toolName]`/`where[threadId]`/`where[actorRef]`/`where[agentName]`/\n * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.\n *\n * Unlike `GET approvals` this reports `total`, so the console can say \"showing N of M\" instead of\n * silently hiding pending decisions behind a row cap.\n */\n @Get('approvals-page')\n approvalsPage(\n @Query('page') page?: string,\n @Query('limit') limit?: string,\n @Query('where') where?: Record<string, string>,\n ): Promise<GovernancePage<PendingApprovalRow>> {\n const parsedWhere = parseApprovalWhere(where);\n return this.dashboard.approvalsPage({\n page: parsePageNumber(page),\n pageSize: parseLimit(limit, 25),\n ...(parsedWhere !== undefined ? { where: parsedWhere } : {}),\n });\n }\n\n /**\n * Decide a pending HITL tool call. Body `{ approved: boolean; reason?: string }`. 501s (via\n * `DashboardService.decideApproval`) when no `AGENT_APPROVAL_PORT` is bound. `executedByRef`\n * comes from {@link deciderRef} run against the live request.\n */\n @Post('approvals/:toolCallId')\n @HttpCode(204)\n async decideApproval(\n @Param('toolCallId') toolCallId: string,\n @Body() body: unknown,\n @Req() req: unknown,\n ): Promise<void> {\n await this.dashboard.decideApproval(toolCallId, body, await this.deciderRef(req));\n }\n\n /**\n * WHO decided, as an opaque ref: an explicit `approvalActorRef` override wins outright (no\n * resolver fallback, even when it returns `undefined`); otherwise the AgentModule-configured\n * actor resolver — the same identity seam chat requests use. A resolver that throws is an\n * unauthenticated/unreadable request, not an error to surface: the decision itself was already\n * authorized by the dashboard's guards, so the ref is simply omitted.\n */\n private async deciderRef(req: unknown): Promise<string | undefined> {\n if (this.approvalActorRef !== undefined) {\n return this.approvalActorRef(req);\n }\n if (this.actorResolver === undefined) {\n return undefined;\n }\n try {\n return (await this.actorResolver.resolve(req)).id;\n } catch {\n return undefined;\n }\n }\n\n /** Per-tool call/failure/rejection/latency rollup for a day range (defaults to the last 30 days). */\n @Get('tools')\n tools(@Query('from') from?: string, @Query('to') to?: string): Promise<ToolStatRow[]> {\n return this.dashboard.toolStats(resolveRange(from, to));\n }\n\n /**\n * Generative-UI telemetry for a day range (defaults to the last 30 days): `{ totals, byToolModel,\n * errors, byDay, costPerTurn, latency }`. 501s (via `DashboardService.genuiStats`) when the bound\n * store does not implement `genuiStats`.\n */\n @Get('genui')\n genui(@Query('from') from?: string, @Query('to') to?: string): Promise<GenuiStats> {\n return this.dashboard.genuiStats(resolveRange(from, to));\n }\n\n /** Most recent threads (default 50, max 200) with rolled-up counts. */\n @Get('threads')\n threads(@Query('limit') limit?: string): Promise<ThreadActivityRowWithLabel[]> {\n return this.dashboard.recentThreads(parseLimit(limit, 50));\n }\n\n /**\n * Paged, filterable threads for the Runs & tools threads table. `page` (default 1), `limit`\n * (default 25, max 200), and `where[actorRef]`/`where[title]` (case-insensitive substring)/\n * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.\n */\n @Get('threads-page')\n threadsPage(\n @Query('page') page?: string,\n @Query('limit') limit?: string,\n @Query('where') where?: Record<string, string>,\n ): Promise<GovernancePage<ThreadActivityRowWithLabel>> {\n const parsedWhere = parseThreadWhere(where);\n return this.dashboard.threadsPage({\n page: parsePageNumber(page),\n pageSize: parseLimit(limit, 25),\n ...(parsedWhere !== undefined ? { where: parsedWhere } : {}),\n });\n }\n\n /**\n * One thread drill-down: the thread (actor-labeled), its lifetime token/cost rollup, its newest\n * runs and its newest messages, in ONE round trip. `messages` (default 50, max 200) and `runs`\n * (default 25, max 200) cap the two lists; `runTotal` and `thread.messageCount` report the true\n * sizes so the console can say what the caps hid. 404s on an unknown thread id.\n *\n * A soft-deleted thread is still returned, flagged `deleted: true` — an audit needs the thread it\n * just lost, and the alternative is a drill-down that 404s on exactly the interesting case.\n */\n @Get('threads/:threadId')\n threadDetail(\n @Param('threadId') threadId: string,\n @Query('messages') messages?: string,\n @Query('runs') runs?: string,\n ): Promise<ThreadDetailWithLabel> {\n return this.dashboard.threadDetail({\n threadId,\n messageLimit: parseLimit(messages, 50),\n runLimit: parseLimit(runs, 25),\n });\n }\n\n /**\n * Current price row per model, for the pricing tab. 501s (via `DashboardService.listPrices`) when\n * no `AGENT_PRICING_STORE` is bound.\n */\n @Get('pricing')\n listPrices(): Promise<CurrentModelPrice[]> {\n return this.dashboard.listPrices();\n }\n\n /**\n * Set a model's current price. Body shape mirrors core's `ModelPriceInput`\n * (`{ modelId, inputPricePer1m, outputPricePer1m, cacheWritePricePer1m?, cacheReadPricePer1m? }`).\n * 501s when no `AGENT_PRICING_STORE` is bound; 400s on a malformed body.\n */\n @Post('pricing')\n upsertPrice(@Body() body: unknown): Promise<void> {\n return this.dashboard.upsertPrice(body);\n }\n\n /** Server-Sent Events stream of live `aviary:agent:*` events — the Live feed tails it. */\n @Sse('stream')\n stream(): Observable<{ data: LiveAgentEvent }> {\n return this.dashboard.streamEvents();\n }\n}\n","import { subscribe, unsubscribe } from 'node:diagnostics_channel';\nimport type {\n ActorSpendRow,\n AgentApprovalPort,\n AgentDiagnosticEvent,\n AgentGovernanceQueries,\n AgentPricingStore,\n ApprovalWhere,\n CurrentModelPrice,\n FeatureSpendRow,\n GenuiStats,\n GovernancePage,\n GovernancePageQuery,\n GovernanceRange,\n GovernanceRunDetail,\n GovernanceThreadDetail,\n GovernanceThreadDetailQuery,\n ModelSpendRow,\n PendingApprovalRow,\n RecentRunRow,\n RunAgentBreakdownRow,\n RunErrorBreakdownRow,\n RunMetrics,\n RunTrendPoint,\n RunWhere,\n ThreadActivityRow,\n ThreadSpendRow,\n ThreadWhere,\n ToolCallActivityRow,\n ToolCallWhere,\n ToolStatRow,\n UsageTrendPoint,\n} from '@dudousxd/nestjs-agent-core';\nimport { channelName } from '@dudousxd/nestjs-diagnostics';\nimport {\n Inject,\n Injectable,\n NotFoundException,\n NotImplementedException,\n Optional,\n} from '@nestjs/common';\nimport { Observable } from 'rxjs';\nimport type { ActorDirectory } from './actor-directory.js';\nimport { parseApprovalDecision } from './parse-approval-decision.js';\nimport { parsePriceInput } from './parse-price-input.js';\nimport {\n AGENT_ACTOR_DIRECTORY,\n AGENT_APPROVAL_PORT,\n AGENT_GOVERNANCE_QUERIES,\n AGENT_PRICING_STORE,\n} from './tokens.js';\n\n/** An actor-scoped row decorated with its resolved display label — `null` when unbound or unresolved. */\nexport interface WithActorLabel {\n actorLabel: string | null;\n}\n\nexport type ActorSpendRowWithLabel = ActorSpendRow & WithActorLabel;\nexport type ThreadSpendRowWithLabel = ThreadSpendRow & WithActorLabel;\nexport type ThreadActivityRowWithLabel = ThreadActivityRow & WithActorLabel;\n\n/**\n * `GET <api>/runs/:runId` — a run drill-down with the owning thread's `actorLabel` resolved, the\n * same way {@link threadsPage} labels its rows. The run row itself stays unlabeled, matching every\n * other run surface in this console.\n */\nexport type RunDetailWithLabel = Omit<GovernanceRunDetail, 'thread'> & {\n thread: GovernanceRunDetail['thread'] & WithActorLabel;\n};\n\n/** `GET <api>/threads/:threadId` — a thread drill-down, actor-labeled like {@link threadsPage}. */\nexport type ThreadDetailWithLabel = Omit<GovernanceThreadDetail, 'thread'> & {\n thread: ThreadActivityRowWithLabel;\n};\n\n/** The spend/usage overview the SPA renders on its headline section (`GET <api>/spend`). */\nexport interface SpendOverview {\n byModel: ModelSpendRow[];\n byActor: ActorSpendRowWithLabel[];\n trend: UsageTrendPoint[];\n /**\n * Spend per feature — agent turns and usage recorded outside them side by side. Absent when the\n * bound governance adapter has no `spendByFeature`.\n */\n byFeature?: FeatureSpendRow[];\n}\n\n/** The run-reliability overview the SPA renders on its Reliability section (`GET <api>/reliability`). */\nexport interface ReliabilityOverview {\n metrics: RunMetrics;\n byAgent: RunAgentBreakdownRow[];\n errors: RunErrorBreakdownRow[];\n trend: RunTrendPoint[];\n}\n\n/** The message returned as a 501 when no `AGENT_PRICING_STORE` is bound. */\nconst PRICING_STORE_UNBOUND_MESSAGE =\n 'Pricing CRUD is unavailable: no AGENT_PRICING_STORE is bound. Bind a pricing store (e.g. ' +\n 'MikroOrmPricingStore from @dudousxd/nestjs-agent-store-mikro-orm) to enable it.';\n\n/** The message returned as a 501 when no `AGENT_APPROVAL_PORT` is bound. */\nconst APPROVAL_PORT_UNBOUND_MESSAGE =\n 'Approve/reject is unavailable: no AGENT_APPROVAL_PORT is bound. Import AgentModule from ' +\n '@dudousxd/nestjs-agent alongside this dashboard to enable it — the approvals inbox stays ' +\n 'read-only until then.';\n\n/** The message returned as a 501 when the bound read-model does not implement `genuiStats`. */\nconst GENUI_STATS_UNSUPPORTED_MESSAGE =\n 'Generative UI telemetry is unavailable: the bound AGENT_GOVERNANCE_QUERIES store does not ' +\n 'implement genuiStats. Use a store adapter that records generative-UI calls (recordGenuiCalls) ' +\n 'to enable it.';\n\n/** One live agent event forwarded over SSE, flattened from the `aviary:agent:*` diagnostics envelope. */\nexport interface LiveAgentEvent {\n /** The event name, e.g. `run.started` / `tool-call` / `quota.exceeded`. */\n event: string;\n /** Epoch millis the event was emitted. */\n ts: number;\n /** The library-defined payload (see the `Agent*Event` shapes in core's diagnostics). */\n payload: Record<string, unknown>;\n}\n\n/**\n * The `aviary:agent:*` events the Live feed tails — a curated subset of core's\n * `AGENT_DIAGNOSTIC_EVENTS`, typed against it so a renamed event is a compile error here. Every run\n * TERMINAL is in it (finished, failed, cancelled): a feed that showed runs starting but never failing\n * would read as \"all fine\" exactly when it is not. The per-turn bookkeeping events (skills, memory,\n * retrieval, tool retries) are left to the telescope.\n */\nconst AGENT_EVENTS: readonly AgentDiagnosticEvent[] = [\n 'run.started',\n 'message',\n 'tool-call',\n 'quota.exceeded',\n 'run.finished',\n 'run.failed',\n 'run.cancelled',\n 'delegated',\n];\n\n/** The `node:diagnostics_channel` envelope `emit()` publishes (see `@dudousxd/nestjs-diagnostics`). */\ninterface AgentDiagnosticEnvelope {\n event: string;\n ts?: number;\n payload?: Record<string, unknown>;\n}\n\n/** Narrow the untyped diagnostics-channel message to the envelope we forward. */\nfunction isAgentEnvelope(message: unknown): message is AgentDiagnosticEnvelope {\n return (\n typeof message === 'object' &&\n message !== null &&\n 'event' in message &&\n typeof (message as { event: unknown }).event === 'string'\n );\n}\n\n/**\n * Read-model + live bridge backing the AI-gateway console.\n *\n * - Historical, restart-surviving spend/usage/threads come from the injected\n * {@link AGENT_GOVERNANCE_QUERIES} read-model (backed by a store adapter). The host must provide\n * that token — bind it via your `@dudousxd/nestjs-agent` module (global) alongside this dashboard.\n * - Live activity comes off the `aviary:agent:*` diagnostics channel, subscribed per SSE client and\n * unsubscribed when the client disconnects.\n * - `actorLabel` on actor-scoped rows comes from the OPTIONAL {@link AGENT_ACTOR_DIRECTORY} — `null`\n * on every row when nothing is bound, so the console degrades to raw `actorRef`s instead of failing.\n * - Pricing CRUD (`listPrices`/`upsertPrice`) reads/writes the OPTIONAL {@link AGENT_PRICING_STORE} —\n * a 501 with a clear message when nothing is bound.\n */\n@Injectable()\nexport class DashboardService {\n constructor(\n @Inject(AGENT_GOVERNANCE_QUERIES) private readonly queries: AgentGovernanceQueries,\n @Optional()\n @Inject(AGENT_ACTOR_DIRECTORY)\n private readonly actorDirectory?: ActorDirectory,\n @Optional()\n @Inject(AGENT_PRICING_STORE)\n private readonly pricingStore?: AgentPricingStore,\n @Optional()\n @Inject(AGENT_APPROVAL_PORT)\n private readonly approvalPort?: AgentApprovalPort,\n ) {}\n\n /** Spend/usage overview for a day range: by-model + by-actor spend and the daily trend, in parallel. */\n async spend(range: GovernanceRange): Promise<SpendOverview> {\n const [byModel, byActorRaw, trend, byFeature] = await Promise.all([\n this.queries.spendByModel(range),\n this.queries.spendByActor(range),\n this.queries.usageTrend(range),\n this.queries.spendByFeature?.(range),\n ]);\n const byActor = await this.withActorLabels(byActorRaw);\n return { byModel, byActor, trend, ...(byFeature !== undefined ? { byFeature } : {}) };\n }\n\n /** Top threads by cost for a day range (default 10, highest cost first). */\n async topThreads(range: GovernanceRange, limit = 10): Promise<ThreadSpendRowWithLabel[]> {\n const rows = await this.queries.spendByThread(range, limit);\n return this.withActorLabels(rows);\n }\n\n /** Run reliability for a day range: metrics, by-agent/by-error breakdowns and the trend, in parallel. */\n async reliability(range: GovernanceRange): Promise<ReliabilityOverview> {\n const [metrics, byAgent, errors, trend] = await Promise.all([\n this.queries.runMetrics(range),\n this.queries.runsByAgent(range),\n this.queries.runErrors(range),\n this.queries.runTrend(range),\n ]);\n return { metrics, byAgent, errors, trend };\n }\n\n /** Most recent runs (status/agent/duration/error) for the Reliability recent-runs table. */\n recentRuns(limit: number): Promise<RecentRunRow[]> {\n return this.queries.recentRuns(limit);\n }\n\n /** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */\n recentToolCalls(limit: number): Promise<ToolCallActivityRow[]> {\n return this.queries.recentToolCalls(limit);\n }\n\n /** Paged, filterable tool calls for the Runs & tools activity table. */\n toolCallsPage(\n query: GovernancePageQuery<ToolCallWhere>,\n ): Promise<GovernancePage<ToolCallActivityRow>> {\n return this.queries.toolCallsPage(query);\n }\n\n /** Paged, filterable runs for the Reliability recent-runs table. */\n runsPage(query: GovernancePageQuery<RunWhere>): Promise<GovernancePage<RecentRunRow>> {\n return this.queries.runsPage(query);\n }\n\n /**\n * Tool calls sitting `pending_approval`, oldest first, for the cross-thread approvals inbox.\n * Capped and totalless — kept because the SPA still calls `GET approvals`; new callers want\n * {@link approvalsPage}, which reports how much of the backlog the cap hid.\n */\n pendingApprovals(limit: number): Promise<PendingApprovalRow[]> {\n return this.queries.pendingApprovals(limit);\n }\n\n /** Paged, filterable approvals inbox with a total — the backlog-safe read of the HITL queue. */\n approvalsPage(\n query: GovernancePageQuery<ApprovalWhere>,\n ): Promise<GovernancePage<PendingApprovalRow>> {\n return this.queries.approvalsPage(query);\n }\n\n /**\n * One run with its thread and its tool calls. 404s on an unknown id rather than returning an empty\n * detail: \"this run does not exist\" and \"this run did nothing\" are different answers, and a console\n * that conflates them sends an operator looking for a bug that isn't there.\n */\n async runDetail(runId: string): Promise<RunDetailWithLabel> {\n const detail = await this.queries.runDetail(runId);\n if (detail === null) {\n throw new NotFoundException(`No run \"${runId}\".`);\n }\n const [thread] = await this.withActorLabels([detail.thread]);\n // `withActorLabels` returns one row per input; the array is never empty here.\n return { ...detail, thread: thread ?? { ...detail.thread, actorLabel: null } };\n }\n\n /** One thread with its usage rollup, runs and messages. 404s on an unknown id, like {@link runDetail}. */\n async threadDetail(query: GovernanceThreadDetailQuery): Promise<ThreadDetailWithLabel> {\n const detail = await this.queries.threadDetail(query);\n if (detail === null) {\n throw new NotFoundException(`No thread \"${query.threadId}\".`);\n }\n const [thread] = await this.withActorLabels([detail.thread]);\n return { ...detail, thread: thread ?? { ...detail.thread, actorLabel: null } };\n }\n\n /** Per-tool call/failure/rejection/latency rollup for a day range, for the Tools section. */\n toolStats(range: GovernanceRange): Promise<ToolStatRow[]> {\n return this.queries.toolStats(range);\n }\n\n /**\n * Generative-UI telemetry for a day range — first-try success per tool/model, common errors, UI\n * cost per day/turn and time-to-frame. `genuiStats` is OPTIONAL on the read-model (a store that\n * does not record UI calls leaves it out), so an unsupported store 501s with a clear message, the\n * same posture as an unbound pricing store — the console renders that as its own state.\n */\n async genuiStats(range: GovernanceRange): Promise<GenuiStats> {\n if (this.queries.genuiStats === undefined) {\n throw new NotImplementedException(GENUI_STATS_UNSUPPORTED_MESSAGE);\n }\n return this.queries.genuiStats(range);\n }\n\n /**\n * Decide a pending HITL tool call from the console. Routes through the OPTIONAL\n * {@link AGENT_APPROVAL_PORT} — bound by `@dudousxd/nestjs-agent` to the SAME signal path chat\n * approvals use — so a 501 here (checked BEFORE body validation, same ordering as\n * {@link upsertPrice}) means \"no approval port bound\", not \"your body was invalid\". `executedByRef`\n * is an OPAQUE decider ref the caller resolved from the live request (see\n * `AgentDashboardOptions.approvalActorRef`); omitted when the host didn't configure one.\n */\n async decideApproval(\n toolCallId: string,\n body: unknown,\n executedByRef: string | undefined,\n ): Promise<void> {\n if (this.approvalPort === undefined) {\n throw new NotImplementedException(APPROVAL_PORT_UNBOUND_MESSAGE);\n }\n const decision = parseApprovalDecision(body);\n const opts = executedByRef !== undefined ? { executedByRef } : {};\n if (decision.approved) {\n await this.approvalPort.approve(toolCallId, opts);\n return;\n }\n await this.approvalPort.reject(toolCallId, {\n ...opts,\n ...(decision.reason !== undefined ? { reason: decision.reason } : {}),\n });\n }\n\n /** Most recent threads with rolled-up message/token counts. */\n async recentThreads(limit: number): Promise<ThreadActivityRowWithLabel[]> {\n const rows = await this.queries.recentThreads(limit);\n return this.withActorLabels(rows);\n }\n\n /**\n * Paged, filterable threads for the Runs & tools threads table — actor-labeled the same way\n * {@link recentThreads} is.\n */\n async threadsPage(\n query: GovernancePageQuery<ThreadWhere>,\n ): Promise<GovernancePage<ThreadActivityRowWithLabel>> {\n const page = await this.queries.threadsPage(query);\n const rows = await this.withActorLabels(page.rows);\n return { ...page, rows };\n }\n\n /**\n * Decorate actor-scoped rows with `actorLabel`, batching the distinct `actorRef`s into ONE\n * {@link ActorDirectory.resolveDisplay} call per response. `null` for every row when no directory\n * is bound, or for a ref the directory didn't resolve.\n */\n private async withActorLabels<Row extends { actorRef: string }>(\n rows: Row[],\n ): Promise<(Row & WithActorLabel)[]> {\n if (rows.length === 0) {\n return [];\n }\n if (this.actorDirectory === undefined) {\n return rows.map((row) => ({ ...row, actorLabel: null }));\n }\n const refs = [...new Set(rows.map((row) => row.actorRef))];\n const resolved = await this.actorDirectory.resolveDisplay(refs);\n return rows.map((row) => ({ ...row, actorLabel: resolved[row.actorRef] ?? null }));\n }\n\n /** Current price row per model, for the pricing tab. 501s when no `AGENT_PRICING_STORE` is bound. */\n async listPrices(): Promise<CurrentModelPrice[]> {\n if (this.pricingStore === undefined) {\n throw new NotImplementedException(PRICING_STORE_UNBOUND_MESSAGE);\n }\n return this.pricingStore.listCurrentPrices();\n }\n\n /**\n * Set a model's current price (`POST <api>/pricing` body). 501s when no `AGENT_PRICING_STORE` is\n * bound (checked BEFORE body validation, so an unbound store always reports as unimplemented rather\n * than as a validation error); otherwise the body is minimally validated via {@link parsePriceInput}.\n */\n async upsertPrice(body: unknown): Promise<void> {\n if (this.pricingStore === undefined) {\n throw new NotImplementedException(PRICING_STORE_UNBOUND_MESSAGE);\n }\n await this.pricingStore.upsertModelPrice(parsePriceInput(body));\n }\n\n /**\n * Live SSE stream of `aviary:agent:*` diagnostics events. One subscription per SSE client:\n * subscribing wires a handler onto each agent channel; the returned teardown removes them all when\n * the client disconnects (or the observable is otherwise unsubscribed).\n */\n streamEvents(): Observable<{ data: LiveAgentEvent }> {\n return new Observable<{ data: LiveAgentEvent }>((subscriber) => {\n const bindings = AGENT_EVENTS.map((event) => {\n const name = channelName('agent', event);\n const handler = (message: unknown): void => {\n if (!isAgentEnvelope(message)) return;\n subscriber.next({\n data: {\n event: message.event,\n ts: message.ts ?? Date.now(),\n payload: message.payload ?? {},\n },\n });\n };\n subscribe(name, handler);\n return { name, handler };\n });\n return () => {\n for (const binding of bindings) unsubscribe(binding.name, binding.handler);\n };\n });\n }\n}\n","import { BadRequestException } from '@nestjs/common';\n\n/** The parsed, minimally-validated `POST <api>/approvals/:toolCallId` body. */\nexport interface ApprovalDecisionInput {\n approved: boolean;\n reason?: string;\n}\n\n/**\n * Minimal shape guard for a `POST <api>/approvals/:toolCallId` body — rejects junk before it reaches\n * `AgentApprovalPort.approve`/`reject`. Mirrors `parsePriceInput`'s role for the pricing endpoint.\n */\nexport function parseApprovalDecision(body: unknown): ApprovalDecisionInput {\n if (typeof body !== 'object' || body === null) {\n throw new BadRequestException('Expected a JSON object body.');\n }\n const { approved, reason } = body as Record<string, unknown>;\n\n if (typeof approved !== 'boolean') {\n throw new BadRequestException('\"approved\" must be a boolean.');\n }\n if (reason !== undefined && typeof reason !== 'string') {\n throw new BadRequestException('\"reason\" must be a string when present.');\n }\n\n return { approved, ...(reason !== undefined ? { reason } : {}) };\n}\n","import type { ModelPriceInput } from '@dudousxd/nestjs-agent-core';\nimport { BadRequestException } from '@nestjs/common';\n\n/**\n * Minimal shape guard for a `POST <api>/pricing` body — rejects junk before it reaches\n * `AgentPricingStore.upsertModelPrice`. Not a full schema validator (the store adapter owns real\n * constraints, e.g. uniqueness); this only checks the wire shape core's `ModelPriceInput` requires.\n */\nexport function parsePriceInput(body: unknown): ModelPriceInput {\n if (typeof body !== 'object' || body === null) {\n throw new BadRequestException('Expected a JSON object body.');\n }\n const { modelId, inputPricePer1m, outputPricePer1m, cacheWritePricePer1m, cacheReadPricePer1m } =\n body as Record<string, unknown>;\n\n if (typeof modelId !== 'string' || modelId.trim().length === 0) {\n throw new BadRequestException('\"modelId\" must be a non-empty string.');\n }\n if (!isFiniteNonNegative(inputPricePer1m)) {\n throw new BadRequestException('\"inputPricePer1m\" must be a non-negative number.');\n }\n if (!isFiniteNonNegative(outputPricePer1m)) {\n throw new BadRequestException('\"outputPricePer1m\" must be a non-negative number.');\n }\n if (cacheWritePricePer1m !== undefined && !isFiniteNonNegative(cacheWritePricePer1m)) {\n throw new BadRequestException(\n '\"cacheWritePricePer1m\" must be a non-negative number when present.',\n );\n }\n if (cacheReadPricePer1m !== undefined && !isFiniteNonNegative(cacheReadPricePer1m)) {\n throw new BadRequestException(\n '\"cacheReadPricePer1m\" must be a non-negative number when present.',\n );\n }\n\n return {\n modelId,\n inputPricePer1m,\n outputPricePer1m,\n ...(cacheWritePricePer1m !== undefined ? { cacheWritePricePer1m } : {}),\n ...(cacheReadPricePer1m !== undefined ? { cacheReadPricePer1m } : {}),\n };\n}\n\nfunction isFiniteNonNegative(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0;\n}\n","/**\n * DI tokens for the standalone AI-gateway dashboard.\n *\n * All use `Symbol.for(...)` (the global symbol registry) on purpose: pnpm peer multiplexing + dual\n * ESM/CJS can load a package more than once, and a plain `Symbol()` would mint a distinct token per\n * copy and break DI across the ESM/CJS split. A registered symbol collapses every copy onto the same\n * token.\n */\n\n/**\n * The governance read-model, owned by `@dudousxd/nestjs-agent-core`. We re-declare it here BY VALUE\n * (not by import) so DI does not depend on a runtime value-import of core resolving — `Symbol.for`\n * with the identical key resolves to the SAME symbol instance as core's own\n * `packages/core/src/tokens.ts` export. The key MUST stay byte-identical with that export.\n */\nexport const AGENT_GOVERNANCE_QUERIES = Symbol.for('@dudousxd/nestjs-agent:governance-queries');\n\n/**\n * Optional actor→label resolver (see {@link ActorDirectory} in `./actor-directory.js`), owned by\n * `@dudousxd/nestjs-agent-core`. Re-declared here BY VALUE for the same reason as\n * {@link AGENT_GOVERNANCE_QUERIES} above — the key MUST stay byte-identical with core's own\n * `AGENT_ACTOR_DIRECTORY` export so both copies collapse onto the same registered symbol. Optional:\n * the dashboard works with actorRef-only rows when nothing is bound.\n */\nexport const AGENT_ACTOR_DIRECTORY = Symbol.for('@dudousxd/nestjs-agent:actor-directory');\n\n/**\n * The pricing WRITE side (`AgentPricingStore`), owned by `@dudousxd/nestjs-agent-core`. Re-declared\n * here BY VALUE for the same reason as {@link AGENT_GOVERNANCE_QUERIES} above. Optional: the pricing\n * tab/endpoints 501 with a clear message when nothing is bound.\n */\nexport const AGENT_PRICING_STORE = Symbol.for('@dudousxd/nestjs-agent:pricing-store');\n\n/**\n * Console-side HITL decisions (`AgentApprovalPort`), owned by `@dudousxd/nestjs-agent-core`.\n * Re-declared here BY VALUE for the same reason as {@link AGENT_GOVERNANCE_QUERIES} above. Optional:\n * bound by `@dudousxd/nestjs-agent` (which adapts it to the same signal path chat approvals use) —\n * absent, `POST <api>/approvals/:toolCallId` 501s and the approvals inbox renders read-only.\n */\nexport const AGENT_APPROVAL_PORT = Symbol.for('@dudousxd/nestjs-agent:approval-port');\n\n/**\n * The app's request-identity seam (`ActorResolver`), owned by `@dudousxd/nestjs-agent-core` and\n * bound + exported by the (global) `AgentModule` from the host's `actorResolver` option. Re-declared\n * here BY VALUE for the same reason as {@link AGENT_GOVERNANCE_QUERIES} above. Optional: the default\n * decider attribution for `POST <api>/approvals/:toolCallId` — absent (or throwing, i.e.\n * unauthenticated), `executedByRef` is simply omitted.\n */\nexport const AGENT_ACTOR_RESOLVER = Symbol.for('@dudousxd/nestjs-agent:actor-resolver');\n\n/** DI token carrying the UI mount base (e.g. `/ai-gateway`). */\nexport const DASHBOARD_BASE_PATH = Symbol.for('@dudousxd/nestjs-agent-dashboard:base-path');\n\n/** DI token carrying the JSON API base the SPA fetches from (e.g. `/ai-gateway/api`). */\nexport const DASHBOARD_API_PATH = Symbol.for('@dudousxd/nestjs-agent-dashboard:api-path');\n\n/**\n * DI token carrying the host-provided {@link AgentDashboardOptions.approvalActorRef} extractor (or\n * `undefined` when the host didn't set one — the controller then falls back to\n * {@link AGENT_ACTOR_RESOLVER} for decider attribution). Threaded to `AgentApiModule` (where the API\n * controller actually lives) same as the pattern above — `useValue` even when `undefined`, so\n * injecting it needs no `@Optional()`.\n */\nexport const DASHBOARD_APPROVAL_ACTOR_REF = Symbol.for(\n '@dudousxd/nestjs-agent-dashboard:approval-actor-ref',\n);\n\n/** DI token carrying the console's `authorize` (and whether to warn when it is omitted). */\nexport const DASHBOARD_CONSOLE_OPTIONS = Symbol.for(\n '@dudousxd/nestjs-agent-dashboard:console-options',\n);\n\n/** DI token: whether the agent's dev tools register into the dev-tools slot. */\nexport const DASHBOARD_DEV_TOOLS = Symbol.for('@dudousxd/nestjs-agent-dashboard:dev-tools');\n","import type {\n ApprovalWhere,\n RunWhere,\n ThreadWhere,\n ToolCallWhere,\n} from '@dudousxd/nestjs-agent-core';\nimport { BadRequestException } from '@nestjs/common';\n\nconst ISO_DAY = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Parse a `page` query param (1-based); falls back to 1 when absent or not a positive integer. */\nexport function parsePageNumber(value: string | undefined): number {\n const parsed = value === undefined ? Number.NaN : Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed < 1) return 1;\n return parsed;\n}\n\n/** Rejects a `where[<field>]` whose field name isn't in `allowed`, naming the offending field. */\nfunction assertKnownFields(raw: Record<string, string>, allowed: readonly string[]): void {\n for (const field of Object.keys(raw)) {\n if (!allowed.includes(field)) {\n throw new BadRequestException(`Unknown where field \"${field}\".`);\n }\n }\n}\n\n/** Rejects a day-bound `where[<field>]` that isn't `YYYY-MM-DD`, mirroring the range endpoints' check. */\nfunction assertDayFields(raw: Record<string, string>, dayFields: readonly string[]): void {\n for (const field of dayFields) {\n const value = raw[field];\n if (value !== undefined && !ISO_DAY.test(value)) {\n throw new BadRequestException(`\"where[${field}]\" must be YYYY-MM-DD.`);\n }\n }\n}\n\nconst DAY_FIELDS = ['fromDay', 'toDay'] as const;\nconst TOOL_CALL_WHERE_FIELDS = [\n 'toolName',\n 'toolType',\n 'status',\n 'threadId',\n 'fromDay',\n 'toDay',\n] as const;\n\n/**\n * Parse `GET tool-calls-page`'s `where[...]` query params into a `ToolCallWhere`. `undefined` (no\n * `where` params at all) means no filter. Throws `BadRequestException` on an unknown field name or a\n * malformed day bound.\n */\nexport function parseToolCallWhere(\n raw: Record<string, string> | undefined,\n): ToolCallWhere | undefined {\n if (raw === undefined || Object.keys(raw).length === 0) return undefined;\n assertKnownFields(raw, TOOL_CALL_WHERE_FIELDS);\n assertDayFields(raw, DAY_FIELDS);\n return {\n ...(raw.toolName !== undefined ? { toolName: raw.toolName } : {}),\n ...(raw.toolType !== undefined ? { toolType: raw.toolType } : {}),\n ...(raw.status !== undefined ? { status: raw.status } : {}),\n ...(raw.threadId !== undefined ? { threadId: raw.threadId } : {}),\n ...(raw.fromDay !== undefined ? { fromDay: raw.fromDay } : {}),\n ...(raw.toDay !== undefined ? { toDay: raw.toDay } : {}),\n };\n}\n\nconst THREAD_WHERE_FIELDS = ['actorRef', 'title', 'fromDay', 'toDay'] as const;\n\n/**\n * Parse `GET threads-page`'s `where[...]` query params into a `ThreadWhere`. `undefined` means no\n * filter. Throws `BadRequestException` on an unknown field name or a malformed day bound.\n */\nexport function parseThreadWhere(raw: Record<string, string> | undefined): ThreadWhere | undefined {\n if (raw === undefined || Object.keys(raw).length === 0) return undefined;\n assertKnownFields(raw, THREAD_WHERE_FIELDS);\n assertDayFields(raw, DAY_FIELDS);\n return {\n ...(raw.actorRef !== undefined ? { actorRef: raw.actorRef } : {}),\n ...(raw.title !== undefined ? { title: raw.title } : {}),\n ...(raw.fromDay !== undefined ? { fromDay: raw.fromDay } : {}),\n ...(raw.toDay !== undefined ? { toDay: raw.toDay } : {}),\n };\n}\n\n// `threadId` was supported by every adapter's `RunWhere` but never accepted here, so \"show me this\n// thread's runs\" — the drill-down's most obvious follow-up query — 400'd with \"Unknown where field\".\nconst RUN_WHERE_FIELDS = [\n 'agentName',\n 'status',\n 'errorCode',\n 'threadId',\n 'fromDay',\n 'toDay',\n] as const;\n\n/**\n * Parse `GET runs-page`'s `where[...]` query params into a `RunWhere`. `undefined` means no filter.\n * Throws `BadRequestException` on an unknown field name or a malformed day bound.\n */\nexport function parseRunWhere(raw: Record<string, string> | undefined): RunWhere | undefined {\n if (raw === undefined || Object.keys(raw).length === 0) return undefined;\n assertKnownFields(raw, RUN_WHERE_FIELDS);\n assertDayFields(raw, DAY_FIELDS);\n return {\n ...(raw.agentName !== undefined ? { agentName: raw.agentName } : {}),\n ...(raw.status !== undefined ? { status: raw.status } : {}),\n ...(raw.errorCode !== undefined ? { errorCode: raw.errorCode } : {}),\n ...(raw.threadId !== undefined ? { threadId: raw.threadId } : {}),\n ...(raw.fromDay !== undefined ? { fromDay: raw.fromDay } : {}),\n ...(raw.toDay !== undefined ? { toDay: raw.toDay } : {}),\n };\n}\n\nconst APPROVAL_WHERE_FIELDS = [\n 'toolName',\n 'threadId',\n 'actorRef',\n 'agentName',\n 'fromDay',\n 'toDay',\n] as const;\n\n/**\n * Parse `GET approvals-page`'s `where[...]` query params into an `ApprovalWhere`. `undefined` means\n * no filter. Throws `BadRequestException` on an unknown field name or a malformed day bound.\n */\nexport function parseApprovalWhere(\n raw: Record<string, string> | undefined,\n): ApprovalWhere | undefined {\n if (raw === undefined || Object.keys(raw).length === 0) return undefined;\n assertKnownFields(raw, APPROVAL_WHERE_FIELDS);\n assertDayFields(raw, DAY_FIELDS);\n return {\n ...(raw.toolName !== undefined ? { toolName: raw.toolName } : {}),\n ...(raw.threadId !== undefined ? { threadId: raw.threadId } : {}),\n ...(raw.actorRef !== undefined ? { actorRef: raw.actorRef } : {}),\n ...(raw.agentName !== undefined ? { agentName: raw.agentName } : {}),\n ...(raw.fromDay !== undefined ? { fromDay: raw.fromDay } : {}),\n ...(raw.toDay !== undefined ? { toDay: raw.toDay } : {}),\n };\n}\n","/**\n * Leading slash, no trailing slash (`'ai-gateway/'` -> `'/ai-gateway'`). Shared by\n * {@link AgentDashboardModule.forRoot} and {@link agentDashboardMountPaths} so the mount-path math\n * behind the module and the pure helper that mirrors it can never drift apart.\n */\nexport function normalizeDashboardPath(path: string): string {\n return `/${path.replace(/^\\/+|\\/+$/g, '')}`;\n}\n","import { normalizeDashboardPath } from './normalize-path.js';\n\n/** Same shape {@link AgentDashboardModule.forRoot} accepts — kept local so this stays a pure, DI-free helper. */\nexport interface AgentDashboardMountPathsOptions {\n basePath?: string;\n apiBasePath?: string;\n}\n\n/** Strip the leading slash `normalizeDashboardPath` adds — `setGlobalPrefix`'s `exclude` roots are unprefixed. */\nfunction unprefixed(path: string): string {\n return path.replace(/^\\/+/, '');\n}\n\n/**\n * Route roots a host must EXCLUDE from a global prefix (`setGlobalPrefix('api', { exclude })`) so\n * the AI-gateway dashboard's SPA and JSON API keep resolving at their configured mount paths instead\n * of being shifted under the prefix.\n *\n * Unlike a single-surface dashboard (e.g. `telescopeMountPaths()`), this one mounts TWO route roots —\n * the UI at `basePath` and its JSON API at `apiBasePath` — so excluding only one leaves the other\n * shadowed. `options` mirrors {@link AgentDashboardOptions} and resolves through the exact same\n * defaulting (`apiBasePath` falls back to `<basePath>/api`) as {@link AgentDashboardModule.forRoot},\n * so the excluded roots always agree with what actually got mounted.\n *\n * @example\n * ```ts\n * // Raw defaults (basePath `/ai-gateway`, apiBasePath `/ai-gateway/api`):\n * app.setGlobalPrefix('api', { exclude: agentDashboardMountPaths() });\n * // -> ['ai-gateway', 'ai-gateway/{*splat}', 'ai-gateway/api', 'ai-gateway/api/{*splat}']\n *\n * // The recommended pattern — apiBasePath nested under the app's own `/api` prefix — MUST pass the\n * // same options given to `forRoot(...)`:\n * const dashboardOptions = { apiBasePath: '/api/ai-gateway' };\n * app.setGlobalPrefix('api', { exclude: agentDashboardMountPaths(dashboardOptions) });\n * // -> ['ai-gateway', 'ai-gateway/{*splat}', 'api/ai-gateway', 'api/ai-gateway/{*splat}']\n * ```\n */\nexport function agentDashboardMountPaths(options?: AgentDashboardMountPathsOptions): string[] {\n const basePath = normalizeDashboardPath(options?.basePath ?? '/ai-gateway');\n const apiBasePath = normalizeDashboardPath(options?.apiBasePath ?? `${basePath}/api`);\n const base = unprefixed(basePath);\n const api = unprefixed(apiBasePath);\n return [base, `${base}/{*splat}`, api, `${api}/{*splat}`];\n}\n","import { withControllerDecorators } from '@dudousxd/nestjs-agent-core';\nimport 'reflect-metadata';\nimport { type DynamicModule, Module, UseFilters, UseGuards } from '@nestjs/common';\nimport { RouterModule } from '@nestjs/core';\nimport { AgentApiController } from './agent-api.controller.js';\nimport { AgentUiController } from './agent-ui.controller.js';\nimport {\n ConsoleAuthorizeFilter,\n ConsoleAuthorizeGuard,\n DashboardConsoleAccess,\n type DashboardConsoleOptions,\n} from './console-authorize.guard.js';\nimport type { ConsoleAuthorize } from './console-authorize.js';\nimport { AgentConsoleRegistration, AgentConsolesController } from './console-switcher.js';\nimport { DashboardService } from './dashboard.service.js';\nimport { AgentDevToolsRegistration } from './dev-tools.js';\nimport { normalizeDashboardPath } from './normalize-path.js';\nimport {\n DASHBOARD_API_PATH,\n DASHBOARD_APPROVAL_ACTOR_REF,\n DASHBOARD_BASE_PATH,\n DASHBOARD_CONSOLE_OPTIONS,\n DASHBOARD_DEV_TOOLS,\n} from './tokens.js';\n\n/**\n * `TReq` mirrors core's `ActorResolver<TReq>` convention: the transport request type\n * {@link AgentDashboardOptions.approvalActorRef} receives. Defaults to `unknown`.\n */\nexport interface AgentDashboardOptions<TReq = unknown> {\n /**\n * Where the SPA (UI) is served. Default `/ai-gateway`. This is a page route — keep it out of an\n * `/api` prefix so it reads as a UI, not an endpoint.\n */\n basePath?: string;\n /**\n * Where the JSON API is mounted (what the SPA fetches). Default `<basePath>/api`. Set it under\n * your app's `/api` prefix — e.g. `/api/ai-gateway` — so the API inherits the app's auth/proxy\n * rules while the UI stays at `basePath`.\n */\n apiBasePath?: string;\n /**\n * Who reaches the console (the SPA and every API/SSE route): an inline function\n * `(ctx) => boolean | { allowed } | { redirectTo }`, an `@Injectable()` `ConsoleAuthorizer` class,\n * a lazy import of one, `{ policy, ability }` (`@nestjs/authorization`), or a gate name\n * (`@dudousxd/nestjs-authz`). `authorizeByToken({ token })` checks a bearer token.\n *\n * Omitted: open outside production; in production every request answers 403 (and a warning is\n * logged at boot).\n */\n authorize?: ConsoleAuthorize;\n /**\n * Class decorators applied to every controller this module mounts, before the console's own\n * `authorize` check — e.g. `[UseGuards(AuthenticationGuard)]` so `request.user` is set when\n * `authorize` runs. Applied to a per-registration subclass, so nothing sticks to the shared\n * classes. They do not replace `authorize`. Static wiring.\n */\n decorators?: ClassDecorator[];\n /**\n * `false` mounts none of the console's routes, while its services still run — the way NestJS's\n * own packages leave routes to you: serve them from controllers of your own, protected with the\n * official tools, by extending the exported `AgentApiController` / `AgentUiController` under your\n * own path and decorators (the module exports what they inject); `UseConsoleAuthorize()` applies\n * this module's `authorize` to them. Static wiring.\n */\n routes?: boolean;\n /** Extra `imports` merged into the console's modules — what a decorator's guard injects. */\n imports?: DynamicModule['imports'];\n /**\n * OVERRIDE for WHO is deciding a HITL approval — invoked on the incoming\n * `POST <api>/approvals/:toolCallId` request to stamp `AgentApprovalPort`'s `opts.executedByRef`.\n *\n * DEFAULT (option omitted): the AgentModule-configured actor resolver (`AGENT_ACTOR_RESOLVER`) is\n * consulted — the same identity seam chat requests use — and its actor id becomes the decider ref\n * (a throwing resolver, i.e. an unauthenticated request, just omits the ref). Set this only when\n * the console's caller is identified differently from chat's; returning `undefined` leaves\n * `executedByRef` unset — the explicit override wins outright, with no resolver fallback.\n */\n approvalActorRef?: (req: TReq) => string | undefined;\n /**\n * Register the console's reads as developer tools on the agent MCP server's dev endpoint\n * (`/mcp/aviary`), gated by this console's `authorize`. Default: on when the console's routes are\n * mounted, off with `routes: false`.\n */\n devTools?: boolean;\n}\n\n/** Leading slash, no trailing slash. */\nfunction normalize(path: string): string {\n return normalizeDashboardPath(path);\n}\n\n/** The decorators every mounted controller gets: the app's, then the console's access check. */\nfunction consoleDecorators(decorators: ClassDecorator[] | undefined): ClassDecorator[] {\n return [\n ...(decorators ?? []),\n UseGuards(ConsoleAuthorizeGuard),\n UseFilters(ConsoleAuthorizeFilter),\n ];\n}\n\n/** Holds the JSON API + SSE controller and its read service, mounted on its own path. */\n@Module({})\nexport class AgentApiModule {\n static register<TReq = unknown>(options: {\n imports?: DynamicModule['imports'];\n decorators?: ClassDecorator[];\n routes?: boolean;\n approvalActorRef?: (req: TReq) => string | undefined;\n authorize?: ConsoleAuthorize;\n devTools?: boolean;\n }): DynamicModule {\n // The warning for an omitted `authorize` logs with `routes: false` too: the exported guard still\n // answers 403 in production.\n const consoleOptions: DashboardConsoleOptions = {\n ...(options.authorize !== undefined ? { authorize: options.authorize } : {}),\n };\n return {\n module: AgentApiModule,\n imports: [...(options.imports ?? [])],\n controllers:\n options.routes === false\n ? []\n : withControllerDecorators(\n [AgentApiController, AgentConsolesController],\n consoleDecorators(options.decorators),\n ),\n providers: [\n DashboardService,\n // `useValue` even when `options.approvalActorRef` is `undefined` — AgentApiController\n // injects this WITHOUT `@Optional()`.\n { provide: DASHBOARD_APPROVAL_ACTOR_REF, useValue: options.approvalActorRef },\n { provide: DASHBOARD_CONSOLE_OPTIONS, useValue: consoleOptions },\n DashboardConsoleAccess,\n { provide: DASHBOARD_DEV_TOOLS, useValue: options.devTools ?? options.routes !== false },\n AgentDevToolsRegistration,\n ],\n // What a self-mounted `AgentApiController` subclass injects, too (`routes: false`), and the\n // access check `UseConsoleAuthorize()` runs.\n exports: [DashboardService, DASHBOARD_APPROVAL_ACTOR_REF, DashboardConsoleAccess],\n };\n }\n}\n\n/**\n * The AI-gateway console: the SPA at `basePath` and its JSON API at `apiBasePath`. Who reaches them\n * is `authorize` (see {@link AgentDashboardOptions.authorize}).\n */\n@Module({})\nexport class AgentDashboardModule {\n static forRoot<TReq = unknown>(options: AgentDashboardOptions<TReq> = {}): DynamicModule {\n const basePath = normalize(options.basePath ?? '/ai-gateway');\n const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);\n return {\n module: AgentDashboardModule,\n imports: [\n ...(options.imports ?? []),\n // Spread-only-when-set: exactOptionalPropertyTypes rejects an explicit `undefined`.\n AgentApiModule.register({\n ...(options.imports ? { imports: options.imports } : {}),\n ...(options.decorators ? { decorators: options.decorators } : {}),\n ...(options.routes === false ? { routes: false } : {}),\n ...(options.approvalActorRef ? { approvalActorRef: options.approvalActorRef } : {}),\n ...(options.authorize !== undefined ? { authorize: options.authorize } : {}),\n ...(options.devTools !== undefined ? { devTools: options.devTools } : {}),\n }),\n ...(options.routes === false\n ? []\n : [\n RouterModule.register([\n { path: basePath, module: AgentDashboardModule },\n { path: apiBasePath, module: AgentApiModule },\n ]),\n ]),\n ],\n controllers:\n options.routes === false\n ? []\n : withControllerDecorators([AgentUiController], consoleDecorators(options.decorators)),\n providers: [\n { provide: DASHBOARD_BASE_PATH, useValue: basePath },\n { provide: DASHBOARD_API_PATH, useValue: apiBasePath },\n // The console switcher lists this console only where it serves its own page.\n ...(options.routes === false ? [] : [AgentConsoleRegistration]),\n ],\n // Re-export the API module so its DashboardService reaches importers (e.g. the host's own\n // controllers), and the paths a self-mounted `AgentUiController` subclass injects.\n exports: [AgentApiModule, DASHBOARD_BASE_PATH, DASHBOARD_API_PATH],\n };\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { basename, extname, join, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { CanAnyone } from '@dudousxd/nestjs-agent-core';\nimport {\n Controller,\n Get,\n Header,\n Inject,\n NotFoundException,\n Param,\n Req,\n Res,\n StreamableFile,\n} from '@nestjs/common';\nimport { ConsolePage } from './console-authorize.guard.js';\nimport { AGENT_CONSOLE_ID, consoleSwitcherConfig } from './console-switcher.js';\nimport { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH } from './tokens.js';\n\n/**\n * The slice of the express request the Inertia bounce below reads — structural, so the package\n * needs no express dependency (the API controller similarly takes `@Req() req: unknown`).\n */\ninterface UiPageRequest {\n headers: Record<string, string | string[] | undefined>;\n /** Path + query as received (express `originalUrl`) — what the full-page visit must reload. */\n originalUrl: string;\n}\n\n/** The slice of the express response the Inertia bounce writes (passthrough mode — Nest still sends). */\ninterface UiPageResponse {\n status(code: number): unknown;\n setHeader(name: string, value: string): unknown;\n}\n\n/** The base the SPA bundle was built with (Vite `base`); rewritten to the configured base at serve time. */\nconst BUILD_BASE = '/ai-gateway';\n\n/** The id of the page's JSON config block. */\nexport const AGENT_DASHBOARD_CONFIG_ID = 'agent-dashboard-config';\n\n/** dist/server/agent-ui.controller.js -> ../spa (the Vite build output). */\nfunction spaDir(): string {\n return fileURLToPath(new URL('../spa', import.meta.url));\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.json': 'application/json; charset=utf-8',\n '.woff2': 'font/woff2',\n '.ico': 'image/x-icon',\n};\n\n/**\n * Serves the bundled AI-gateway console SPA at the configured base (+ hashed assets at\n * `<base>/assets`). The path comes from `RouterModule` (set by\n * {@link AgentDashboardModule.forRoot}({ basePath })), so the controller routes are relative.\n *\n * Who reaches it: `authorize` on `AgentDashboardModule.forRoot` (checked by `ConsoleAuthorizeGuard`,\n * after the app's `decorators`). Mounted by the app itself (`routes: false`), apply\n * `UseConsoleAuthorize()` to your subclass.\n */\n@CanAnyone()\n@Controller()\nexport class AgentUiController {\n private readonly dir = spaDir();\n\n constructor(\n @Inject(DASHBOARD_BASE_PATH) private readonly basePath: string,\n @Inject(DASHBOARD_API_PATH) private readonly apiBasePath: string,\n ) {}\n\n // index.html references hash-named bundles, so it MUST NOT be cached (stale bundle = the classic\n // \"stuck loading after a deploy\"). The hashed assets below are immutable.\n @Get()\n @ConsolePage()\n @Header('Content-Type', 'text/html; charset=utf-8')\n @Header('Cache-Control', 'no-store, must-revalidate')\n index(@Req() req: UiPageRequest, @Res({ passthrough: true }) res: UiPageResponse): string {\n // An Inertia <Link> in the host app visits this page route as an XHR expecting an Inertia JSON\n // page object; serving the SPA's HTML instead lands it in the Inertia client's `srcdoc` error\n // modal, where the page's relative asset URLs die on CORS (origin null). The protocol's own\n // escape hatch for \"this URL is not an Inertia page\" is a 409 Conflict carrying\n // `X-Inertia-Location`: the client responds with a full `window.location` visit, which renders\n // the console normally. Only HTML page routes need this — asset binaries are never fetched with\n // the header.\n if (req.headers['x-inertia'] !== undefined) {\n res.status(409);\n res.setHeader('X-Inertia-Location', req.originalUrl);\n return '';\n }\n const indexPath = join(this.dir, 'index.html');\n if (!existsSync(indexPath)) {\n throw new NotFoundException('Dashboard is not built. Run the package build.');\n }\n // The bundle was built with Vite base `/ai-gateway/`; rewrite asset URLs to the configured base\n // so the SPA loads from `<base>/assets` wherever it's mounted, and tell the client its API base.\n const html = readFileSync(indexPath, 'utf8').replaceAll(\n `=\"${BUILD_BASE}/`,\n `=\"${this.basePath}/`,\n );\n // __AGENT_BASE__ = where assets load; __AGENT_API__ = where the SPA fetches the JSON API.\n const inject = `<script>window.__AGENT_BASE__='${this.basePath}';window.__AGENT_API__='${this.apiBasePath}';</script>${this.configBlock()}`;\n return html.includes('</head>') ? html.replace('</head>', `${inject}</head>`) : inject + html;\n }\n\n /**\n * The page's JSON config block (`agent-dashboard-config`, CSP-safe: data, not script). It carries\n * `switcher` only when another Aviary console is registered.\n */\n private configBlock(): string {\n const switcher = consoleSwitcherConfig(AGENT_CONSOLE_ID, `${this.apiBasePath}/consoles`);\n const json = JSON.stringify({\n apiBase: this.apiBasePath,\n ...(switcher !== undefined ? { switcher } : {}),\n }).replaceAll('<', '\\\\u003c');\n return `<script type=\"application/json\" id=\"${AGENT_DASHBOARD_CONFIG_ID}\">${json}</script>`;\n }\n\n @Get('assets/:file')\n @Header('Cache-Control', 'public, max-age=31536000, immutable')\n asset(@Param('file') file: string): StreamableFile {\n const safe = basename(file);\n if (safe !== file) throw new NotFoundException();\n const root = resolve(this.dir, 'assets');\n const assetPath = resolve(root, safe);\n if (!assetPath.startsWith(root + sep) || !existsSync(assetPath)) {\n throw new NotFoundException();\n }\n const type = CONTENT_TYPES[extname(safe)] ?? 'application/octet-stream';\n return new StreamableFile(readFileSync(assetPath), { type });\n }\n}\n","import {\n type ArgumentsHost,\n type CanActivate,\n Catch,\n type ExceptionFilter,\n type ExecutionContext,\n HttpException,\n Inject,\n Injectable,\n type OnModuleInit,\n SetMetadata,\n UseFilters,\n UseGuards,\n applyDecorators,\n} from '@nestjs/common';\nimport { HttpAdapterHost, ModuleRef } from '@nestjs/core';\nimport {\n type ConsoleAuthorize,\n type ConsoleDecide,\n type ConsoleDecision,\n resolveConsoleAuthorize,\n} from './console-authorize.js';\nimport { DASHBOARD_CONSOLE_OPTIONS } from './tokens.js';\n\n/** What {@link DashboardConsoleAccess} is built from (bound per `forRoot`). */\nexport interface DashboardConsoleOptions {\n authorize?: ConsoleAuthorize;\n}\n\n/** Holds the console's resolved `authorize` check for one registration. */\n@Injectable()\nexport class DashboardConsoleAccess implements OnModuleInit {\n #decide: ConsoleDecide | undefined;\n\n constructor(\n @Inject(DASHBOARD_CONSOLE_OPTIONS) private readonly options: DashboardConsoleOptions,\n private readonly moduleRef: ModuleRef,\n ) {}\n\n #resolving: Promise<ConsoleDecide> | undefined;\n\n /** Resolves `authorize` at boot: a configuration mistake fails the boot, and the warning logs once. */\n async onModuleInit(): Promise<void> {\n this.#resolving ??= resolveConsoleAuthorize(this.options.authorize, this.moduleRef);\n this.#decide = await this.#resolving;\n }\n\n /** The decision for one request. */\n async check(context: ExecutionContext): Promise<ConsoleDecision> {\n if (this.#decide === undefined) await this.onModuleInit();\n return (this.#decide as ConsoleDecide)(context);\n }\n}\n\n/** Marks a controller or handler as serving pages: a denied browser navigation gets HTML or a 302. */\nexport const CONSOLE_PAGE_METADATA = '@dudousxd/nestjs-agent-dashboard:console-page';\n\n/**\n * On a controller or a handler that serves pages: a denied `GET` that accepts HTML gets a page (or a\n * 302 for `redirectTo`), not JSON.\n */\nexport const ConsolePage = (): ClassDecorator & MethodDecorator =>\n SetMetadata(CONSOLE_PAGE_METADATA, true);\n\n/** Thrown by {@link ConsoleAuthorizeGuard}; its body is the JSON answer even without the filter. */\nexport class ConsoleAccessDenied extends HttpException {\n constructor(\n readonly decision: Exclude<ConsoleDecision, { allowed: true }>,\n readonly page: boolean,\n ) {\n super(\n decision.reason === 'forbidden'\n ? { error: 'forbidden' }\n : {\n error: 'unauthenticated',\n ...(decision.redirectTo !== undefined ? { redirectTo: decision.redirectTo } : {}),\n },\n decision.reason === 'forbidden' ? 403 : 401,\n );\n }\n}\n\ninterface HttpRequestLike {\n method?: string;\n headers?: Record<string, string | string[] | undefined>;\n}\n\nfunction isPageNavigation(context: ExecutionContext): boolean {\n if (context.getType() !== 'http') return false;\n const page =\n Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getHandler()) === true ||\n Reflect.getMetadata(CONSOLE_PAGE_METADATA, context.getClass()) === true;\n if (!page) return false;\n const req = context.switchToHttp().getRequest<HttpRequestLike>();\n if ((req.method ?? 'GET').toUpperCase() !== 'GET') return false;\n const accept = req.headers?.accept;\n const value = Array.isArray(accept) ? accept.join(',') : (accept ?? '');\n return value.includes('text/html');\n}\n\n/**\n * Runs the console's `authorize` on every route of a controller it guards. Applied by the module\n * after the app's `decorators` (so authentication guards have already set `request.user`); a\n * self-mounted controller (`routes: false`) applies it with {@link UseConsoleAuthorize}.\n */\n@Injectable()\nexport class ConsoleAuthorizeGuard implements CanActivate {\n constructor(private readonly access: DashboardConsoleAccess) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const decision = await this.access.check(context);\n if (decision.allowed) return true;\n throw new ConsoleAccessDenied(decision, isPageNavigation(context));\n }\n}\n\n/** The minimal page a denied browser navigation gets. */\nfunction deniedPage(title: string, message: string): string {\n const body =\n 'font-family:system-ui,sans-serif;display:grid;place-items:center;min-height:100vh;margin:0';\n return `<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>${title}</title></head><body style=\"${body}\"><main style=\"text-align:center\"><h1>${title}</h1><p>${message}</p></main></body></html>`;\n}\n\nconst FORBIDDEN_PAGE = deniedPage('Access denied', 'You do not have access to this console.');\nconst UNAUTHENTICATED_PAGE = deniedPage('Sign in required', 'Sign in to open this console.');\n\n/** Writes a console denial: JSON for API calls; a 302 or an HTML page (403 / 401) for page navigations. */\n@Catch(ConsoleAccessDenied)\n@Injectable()\nexport class ConsoleAuthorizeFilter implements ExceptionFilter<ConsoleAccessDenied> {\n constructor(private readonly adapterHost: HttpAdapterHost) {}\n\n catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void {\n const adapter = this.adapterHost.httpAdapter;\n const res = host.switchToHttp().getResponse<unknown>();\n const { decision, page } = exception;\n if (page && decision.reason === 'unauthenticated' && decision.redirectTo !== undefined) {\n adapter.redirect(res, 302, decision.redirectTo);\n return;\n }\n if (page) {\n const forbidden = decision.reason === 'forbidden';\n adapter.setHeader(res, 'Content-Type', 'text/html; charset=utf-8');\n adapter.reply(res, forbidden ? FORBIDDEN_PAGE : UNAUTHENTICATED_PAGE, forbidden ? 403 : 401);\n return;\n }\n adapter.reply(res, exception.getResponse(), exception.getStatus());\n }\n}\n\n/**\n * The console's access check on a controller of your own (with `routes: false`): the\n * {@link ConsoleAuthorizeGuard} plus the filter that writes its denials. Write it ABOVE your own\n * authentication decorators: class decorators apply bottom-up, so it then runs after them and\n * `request.user` is set.\n */\nexport function UseConsoleAuthorize(): ClassDecorator & MethodDecorator {\n return applyDecorators(UseGuards(ConsoleAuthorizeGuard), UseFilters(ConsoleAuthorizeFilter));\n}\n","import { createHash, timingSafeEqual } from 'node:crypto';\nimport { type ExecutionContext, Logger, type Type } from '@nestjs/common';\nimport { ModuleRef } from '@nestjs/core';\n\n/**\n * What an `authorize` check answers. An allow may name who it let in (`caller`, for example\n * `'user:42'`): the dev MCP endpoint logs it as the caller of a developer tool. Never put a secret\n * in it.\n */\nexport type ConsoleAuthorizeResult =\n | boolean\n | { allowed: true; caller?: string }\n | { allowed: false; reason?: 'unauthenticated' | 'forbidden' }\n | { redirectTo: string };\n\n/** Class form: an @Injectable() provider (constructor injection works). */\nexport interface ConsoleAuthorizer {\n authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;\n}\nexport type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;\n\n/** @nestjs/authorization policy check: policy[ability](user). */\nexport interface ConsolePolicyCheck {\n policy: Type<unknown>;\n ability: string;\n}\n\n/** Who reaches the console. */\nexport type ConsoleAuthorize =\n | ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>) // inline\n | ConsoleAuthorizerClass // class\n | (() => Promise<{ default: ConsoleAuthorizerClass }>) // lazy import\n | ConsolePolicyCheck // official policy\n | string; // registered gate name\n\n/** A normalized `authorize` answer. */\nexport type ConsoleDecision =\n | { allowed: true; caller?: string }\n | { allowed: false; reason: 'unauthenticated' | 'forbidden'; redirectTo?: string };\n\n/** What a resolved `authorize` is: called once per request, never throws. */\nexport type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;\n\n/**\n * The provider that resolves gate names (`authorize: 'console.agent'`), bound globally by\n * `@dudousxd/nestjs-authz`'s `AuthzModule`. A registry symbol, so it is found without depending on\n * that package.\n */\nexport const CONSOLE_GATE = Symbol.for('@dudousxd/aviary:console-gate');\nexport interface ConsoleGate {\n allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;\n}\n\nconst PACKAGE = '@dudousxd/nestjs-agent-dashboard';\nconst MODULE = 'AgentDashboardModule';\nconst logger = new Logger(MODULE);\n\n/** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */\nexport const 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().`;\n\n// Fresh objects on every call: a decision is never a shared, mutable singleton.\nconst allow = (caller?: string): ConsoleDecision =>\n caller !== undefined && caller !== '' ? { allowed: true, caller } : { allowed: true };\nconst forbidden = (): ConsoleDecision => ({ allowed: false, reason: 'forbidden' });\nconst unauthenticated = (): ConsoleDecision => ({ allowed: false, reason: 'unauthenticated' });\n\nfunction isProduction(): boolean {\n return (process.env.NODE_ENV ?? '').trim().toLowerCase() === 'production';\n}\n\n/** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */\nexport function normalizeConsoleResult(result: unknown): ConsoleDecision {\n if (result === true) return allow();\n if (result === null || typeof result !== 'object') return forbidden();\n const value = result as {\n allowed?: unknown;\n reason?: unknown;\n redirectTo?: unknown;\n caller?: unknown;\n };\n if (typeof value.redirectTo === 'string' && value.redirectTo !== '' && value.allowed !== true) {\n return { allowed: false, reason: 'unauthenticated', redirectTo: value.redirectTo };\n }\n if (value.allowed === true) {\n return allow(typeof value.caller === 'string' ? value.caller : undefined);\n }\n return value.reason === 'unauthenticated' ? unauthenticated() : forbidden();\n}\n\n/**\n * A class with `authorize()`: on the prototype, or (an instance-property arrow function,\n * `authorize = (ctx) => ...`) only on instances, so any `class`.\n */\nfunction isAuthorizerClass(value: unknown): value is ConsoleAuthorizerClass {\n if (typeof value !== 'function') return false;\n if (\n typeof (value as { prototype?: { authorize?: unknown } }).prototype?.authorize === 'function'\n ) {\n return true;\n }\n return /^class[\\s{]/.test(Function.prototype.toString.call(value));\n}\n\nfunction isPolicyCheck(value: unknown): value is ConsolePolicyCheck {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as ConsolePolicyCheck).policy === 'function' &&\n typeof (value as ConsolePolicyCheck).ability === 'string'\n );\n}\n\nfunction lazyModuleClass(value: unknown): ConsoleAuthorizerClass | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const candidate = (value as { default?: unknown }).default;\n return isAuthorizerClass(candidate) ? candidate : undefined;\n}\n\n/** A provider registered anywhere (`strict: false`), or `undefined`. */\nfunction tryGet<T>(moduleRef: ModuleRef, token: unknown): T | undefined {\n try {\n return (moduleRef.get(token as never, { strict: false }) as T | undefined) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/** One instance per class per registration: a registered provider, else created with DI. */\nfunction classInstances(moduleRef: ModuleRef) {\n const instances = new Map<ConsoleAuthorizerClass, Promise<ConsoleAuthorizer>>();\n return (Class: ConsoleAuthorizerClass): Promise<ConsoleAuthorizer> => {\n let instance = instances.get(Class);\n if (instance === undefined) {\n const registered = tryGet<ConsoleAuthorizer>(moduleRef, Class);\n instance = registered !== undefined ? Promise.resolve(registered) : moduleRef.create(Class);\n // A failed create is retried on the next request, not cached as a permanent denial.\n instance.catch(() => instances.delete(Class));\n instances.set(Class, instance);\n }\n return instance;\n };\n}\n\n/** `null`, `undefined` and `false` mean \"nobody signed in\", like `@nestjs/authorization`'s guard. */\nfunction hasUser(user: unknown): boolean {\n return user !== null && user !== undefined && user !== false;\n}\n\n/** `{ policy, ability }`: `@nestjs/authorization` (an optional peer) imported lazily, checked at boot. */\nasync function policyDecider(\n check: ConsolePolicyCheck,\n moduleRef: ModuleRef,\n): Promise<(context: ExecutionContext) => Promise<ConsoleDecision>> {\n let mod: typeof import('@nestjs/authorization');\n try {\n mod = await import('@nestjs/authorization');\n } catch {\n throw new Error(\n `${MODULE}: \\`authorize: { policy, ability }\\` needs @nestjs/authorization installed and AuthorizationModule imported; or pass a function, a class, or a gate name.`,\n );\n }\n const service = tryGet<{\n can(policy: Type<unknown>, ability: string, user: unknown): Promise<boolean>;\n }>(moduleRef, mod.AuthorizationService);\n if (service === undefined) {\n throw new Error(\n `${MODULE}: \\`authorize: { policy, ability }\\` needs AuthorizationModule.forRoot() from @nestjs/authorization imported in the app.`,\n );\n }\n const options = tryGet<{ getUser?: (context: ExecutionContext) => unknown }>(\n moduleRef,\n mod.AUTHORIZATION_MODULE_OPTIONS,\n );\n // The user the official `@Can()` sees: the module's `getUser` when one is configured, else the\n // package default (`request.user`). `undefined`, `null` and `false` all mean a guest.\n const getUser = (options?.getUser ?? mod.defaultGetUser) as (\n context: ExecutionContext,\n ) => unknown;\n return async (context) => {\n const user = await getUser(context);\n const signedIn = hasUser(user);\n if (await service.can(check.policy, check.ability, signedIn ? user : null)) return allow();\n return signedIn ? forbidden() : unauthenticated();\n };\n}\n\n/**\n * Resolves `authorize` into one function the guard calls per request. Configuration mistakes (a\n * gate name without nestjs-authz, a policy without `@nestjs/authorization`) throw here, so call it\n * at boot. The returned function never throws: an error is a `forbidden`.\n *\n * `authorize` omitted: open outside production, `forbidden` for every request in production (read\n * when this runs), and one warning logged (unless `warnIfMissing: false`).\n */\nexport async function resolveConsoleAuthorize(\n authorize: ConsoleAuthorize | undefined,\n moduleRef: ModuleRef,\n options: { logger?: Logger; warnIfMissing?: boolean } = {},\n): Promise<ConsoleDecide> {\n const log = options.logger ?? logger;\n if (authorize === undefined) {\n if (options.warnIfMissing !== false) log.warn(CONSOLE_AUTHORIZE_MISSING_WARNING);\n const production = isProduction();\n return async () => (production ? forbidden() : allow('dev-open'));\n }\n\n const instanceOf = classInstances(moduleRef);\n let decide: (context: ExecutionContext) => Promise<unknown>;\n if (typeof authorize === 'string') {\n const name = authorize;\n const gate = tryGet<ConsoleGate>(moduleRef, CONSOLE_GATE);\n if (gate === undefined) {\n throw new Error(\n `${MODULE}: \\`authorize: '${name}'\\` needs @dudousxd/nestjs-authz's AuthzModule (it resolves gate names); or pass a function, a class, or { policy, ability }.`,\n );\n }\n decide = (context) => gate.allows(name, context);\n } else if (isPolicyCheck(authorize)) {\n decide = await policyDecider(authorize, moduleRef);\n } else if (isAuthorizerClass(authorize)) {\n const Class = authorize;\n decide = async (context) => (await instanceOf(Class)).authorize(context);\n } else if (typeof authorize === 'function') {\n const fn = authorize as (context: ExecutionContext) => unknown;\n let lazyClass: ConsoleAuthorizerClass | undefined;\n decide = async (context) => {\n if (lazyClass !== undefined) return (await instanceOf(lazyClass)).authorize(context);\n const result = await fn(context);\n const Class = lazyModuleClass(result);\n if (Class === undefined) return result;\n // A lazy import: remember the class, so the import runs once per registration.\n lazyClass = Class;\n return (await instanceOf(Class)).authorize(context);\n };\n } else {\n throw new Error(\n `${MODULE}: \\`authorize\\` must be a function, a class with authorize(), a lazy import, { policy, ability } or a gate name.`,\n );\n }\n\n return async (context) => {\n try {\n return normalizeConsoleResult(await decide(context));\n } catch (error) {\n log.warn(\n `console \\`authorize\\` threw, denying the request: ${error instanceof Error ? error.message : String(error)}`,\n );\n return forbidden();\n }\n };\n}\n\nfunction sha256(value: string): Buffer {\n return createHash('sha256').update(value).digest();\n}\n\n/**\n * `authorize` by a bearer token: allows when `Authorization: Bearer <token>` matches (compared in\n * constant time), else `unauthenticated`. Header only. An empty token throws here, at config time.\n * The allow names its caller `token:<first 12 hex of sha256(token)>`, never the token.\n */\nexport function authorizeByToken(options: { token: string | undefined }): (\n context: ExecutionContext,\n) => ConsoleAuthorizeResult {\n const token = options.token;\n if (typeof token !== 'string' || token === '') {\n throw new Error('authorizeByToken: `token` is empty; set it (e.g. from an env var).');\n }\n const expected = sha256(token);\n const caller = `token:${expected.toString('hex').slice(0, 12)}`;\n return (context) => {\n const req = context\n .switchToHttp()\n .getRequest<{ headers?: Record<string, string | string[] | undefined> }>();\n const header = req?.headers?.authorization;\n const value = Array.isArray(header) ? header[0] : header;\n const match = typeof value === 'string' ? /^Bearer\\s+(.+)$/i.exec(value.trim()) : null;\n if (match?.[1] !== undefined && timingSafeEqual(sha256(match[1]), expected)) {\n return { allowed: true, caller };\n }\n return { allowed: false, reason: 'unauthenticated' };\n };\n}\n\n/**\n * One `authorize` from several: allowed when any form allows (checked in order, stopping at the\n * first that does, whose `caller` it keeps); otherwise `unauthenticated` when any form said so (keeping the first\n * `redirectTo`), else `forbidden`. Each form is any `authorize` value: a function, a class, a lazy\n * import, `{ policy, ability }` or a gate name.\n *\n * ```ts\n * authorize: anyAuthorize(authorizeByToken({ token: process.env.CONSOLE_TOKEN }), AdminSessionAuthorizer)\n * ```\n */\nexport function anyAuthorize(...forms: ConsoleAuthorize[]): ConsoleAuthorizerClass {\n if (forms.length === 0) throw new Error('anyAuthorize: pass at least one authorize form.');\n class AnyConsoleAuthorizer implements ConsoleAuthorizer {\n #decides: Promise<ConsoleDecide[]> | undefined;\n\n constructor(private readonly moduleRef: ModuleRef) {}\n\n async authorize(context: ExecutionContext): Promise<ConsoleAuthorizeResult> {\n this.#decides ??= Promise.all(\n forms.map((form) =>\n resolveConsoleAuthorize(form, this.moduleRef, { warnIfMissing: false }),\n ),\n );\n // A failed resolution is retried on the next request, not cached as a permanent denial.\n this.#decides.catch(() => {\n this.#decides = undefined;\n });\n let unauthenticated: ConsoleDecision | undefined;\n for (const decide of await this.#decides) {\n const decision = await decide(context);\n if (decision.allowed) return decision;\n if (decision.reason === 'unauthenticated') unauthenticated ??= decision;\n }\n if (unauthenticated === undefined) return { allowed: false, reason: 'forbidden' };\n return 'redirectTo' in unauthenticated && unauthenticated.redirectTo !== undefined\n ? { redirectTo: unauthenticated.redirectTo }\n : { allowed: false, reason: 'unauthenticated' };\n }\n }\n // `ModuleRef` by explicit metadata: the class is created with DI whatever the build emits.\n Reflect.defineMetadata('design:paramtypes', [ModuleRef], AnyConsoleAuthorizer);\n return AnyConsoleAuthorizer;\n}\n","import { CanAnyone } from '@dudousxd/nestjs-agent-core';\nimport {\n Controller,\n type ExecutionContext,\n Get,\n Header,\n Inject,\n Injectable,\n Logger,\n type OnModuleDestroy,\n type OnModuleInit,\n createParamDecorator,\n} from '@nestjs/common';\nimport { DashboardConsoleAccess } from './console-authorize.guard.js';\nimport { DASHBOARD_BASE_PATH } from './tokens.js';\n\n// ── the console registry (v1), re-declared: no Aviary console imports another ──────────────\n\n/** The process-wide slot every Aviary console registers into (created by whichever boots first). */\nexport const AVIARY_CONSOLES_SLOT = Symbol.for('@dudousxd/aviary:consoles');\n\n/** The icon a console shows in the switcher. An unknown value renders as `generic`. */\nexport type AviaryConsoleIcon =\n | 'workflow'\n | 'telescope'\n | 'media'\n | 'payments'\n | 'agent'\n | 'identity'\n | 'generic';\n\n/** What a console's `authorize` answers; only `allowed: true` lists it. */\nexport interface AviaryConsoleDecision {\n allowed: boolean;\n reason?: string;\n redirectTo?: string;\n}\n\n/** One console the switcher can list. */\nexport interface AviaryConsoleEntry {\n /** `/^[a-z][a-z0-9-]*$/`: `'durable'`, `'telescope'`, `'agent'`, `'media'`, … */\n id: string;\n /** Product title: `'Workflows'`, `'Telescope'`, `'Agents'`, `'Media'`. */\n title: string;\n icon: AviaryConsoleIcon;\n /** Same-origin absolute path of the console's page (`/ai-gateway`). */\n path: string;\n /** Sort key: durable 10, telescope 20, agent 30, media 50. */\n order: number;\n /** The console's resolved gate (what its own routes run). Never throws. */\n authorize(context: ExecutionContext): Promise<AviaryConsoleDecision>;\n}\n\n/** The value at {@link AVIARY_CONSOLES_SLOT}. Data only, so every console's copy is identical. */\nexport interface AviaryConsoleRegistry {\n readonly version: 1;\n readonly entries: AviaryConsoleEntry[];\n}\n\n/** One console as `GET <api>/consoles` returns it. */\nexport interface VisibleConsole {\n id: string;\n title: string;\n icon: AviaryConsoleIcon;\n href: string;\n}\n\n/** The answer of `GET <api>/consoles`. */\nexport interface VisibleConsoles {\n version: 1;\n current: string;\n consoles: VisibleConsole[];\n}\n\n/** The `switcher` field of the shell's config block. */\nexport interface ConsoleSwitcherConfig {\n endpoint: string;\n others: number;\n}\n\nconst ID = /^[a-z][a-z0-9-]*$/;\nconst ICONS = new Set<string>([\n 'workflow',\n 'telescope',\n 'media',\n 'payments',\n 'agent',\n 'identity',\n 'generic',\n]);\n\n/** How long another console's `authorize` may take before it is left out. */\nexport const CONSOLE_PROBE_TIMEOUT_MS = 300;\n\n/** Whether `path` is a same-origin absolute path (`/x`; never `//host` or a URL). */\nexport function isSameOriginPath(path: unknown): path is string {\n return typeof path === 'string' && path.startsWith('/') && !path.startsWith('//');\n}\n\n/** The registry, when one is set with a version this copy reads. */\nexport function readAviaryConsoles(): AviaryConsoleRegistry | undefined {\n const value = (globalThis as Record<symbol, unknown>)[AVIARY_CONSOLES_SLOT] as\n | Partial<AviaryConsoleRegistry>\n | undefined;\n if (value?.version !== 1 || !Array.isArray(value.entries)) return undefined;\n return value as AviaryConsoleRegistry;\n}\n\n/**\n * Adds a console to the process's switcher registry, creating it when absent. Returns a function\n * that removes the entry again, or `undefined` when it was not added (another registry version, or\n * the id is taken: logged as a warning). A bad `id` or `path` throws.\n */\nexport function registerAviaryConsole(\n entry: AviaryConsoleEntry,\n logger?: { warn(message: string): void },\n): (() => void) | undefined {\n if (!ID.test(entry.id)) throw new Error(`console id ${JSON.stringify(entry.id)} is invalid`);\n if (!isSameOriginPath(entry.path)) {\n throw new Error(`console path ${JSON.stringify(entry.path)} must start with a single \"/\"`);\n }\n const holder = globalThis as Record<symbol, unknown>;\n if (holder[AVIARY_CONSOLES_SLOT] === undefined) {\n const fresh: AviaryConsoleRegistry = { version: 1, entries: [] };\n holder[AVIARY_CONSOLES_SLOT] = fresh;\n }\n const registry = readAviaryConsoles();\n if (registry === undefined) return undefined;\n if (registry.entries.some((existing) => existing.id === entry.id)) {\n logger?.warn(`console id \"${entry.id}\" is already registered`);\n return undefined;\n }\n registry.entries.push(entry);\n return () => {\n const index = registry.entries.indexOf(entry);\n if (index !== -1) registry.entries.splice(index, 1);\n };\n}\n\n/** The shell's `switcher` config: only when another console is registered (no `authorize` runs). */\nexport function consoleSwitcherConfig(\n selfId: string,\n endpoint: string,\n): ConsoleSwitcherConfig | undefined {\n const registry = readAviaryConsoles();\n if (!registry?.entries.some((entry) => entry.id === selfId)) return undefined;\n const others = registry.entries.filter((entry) => entry.id !== selfId).length;\n return others > 0 ? { endpoint, others } : undefined;\n}\n\n/** Whether `entry` allows the viewer within `ms`; a rejection or a timeout is a no. */\nasync function allows(\n entry: AviaryConsoleEntry,\n context: ExecutionContext,\n ms: number,\n): Promise<boolean> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<boolean>((resolve) => {\n timer = setTimeout(() => resolve(false), ms);\n });\n try {\n return await Promise.race([\n Promise.resolve()\n .then(() => entry.authorize(context))\n .then(\n (decision) => decision?.allowed === true,\n () => false,\n ),\n timeout,\n ]);\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * The consoles other than `selfId` the viewer may open, by `order` then `id`. Every other entry's\n * `authorize` runs in parallel, each given `timeoutMs`; a timeout, a rejection or any denial hides\n * it. No reason is ever returned.\n */\nexport async function listVisibleConsoles(\n registry: AviaryConsoleRegistry | undefined,\n selfId: string,\n context: ExecutionContext,\n timeoutMs = CONSOLE_PROBE_TIMEOUT_MS,\n): Promise<VisibleConsoles> {\n const others = (registry?.entries ?? [])\n .filter((entry) => entry.id !== selfId && isSameOriginPath(entry.path))\n .sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));\n const verdicts = await Promise.all(others.map((entry) => allows(entry, context, timeoutMs)));\n return {\n version: 1,\n current: selfId,\n consoles: others\n .filter((_, index) => verdicts[index])\n .map((entry) => ({\n id: entry.id,\n title: entry.title,\n icon: ICONS.has(entry.icon) ? entry.icon : 'generic',\n href: entry.path,\n })),\n };\n}\n\n// ── the agent console's side ─────────────────────────────────────────────────\n\n/** The agent console's id in the registry. */\nexport const AGENT_CONSOLE_ID = 'agent';\n\nconst logger = new Logger('AgentDashboardModule');\n\n/**\n * Registers the agent console into the switcher registry (the console's page path, gated by its\n * resolved `authorize`). Provided only when the console's routes are mounted; removed on shutdown.\n */\n@Injectable()\nexport class AgentConsoleRegistration implements OnModuleInit, OnModuleDestroy {\n #unregister: (() => void) | undefined;\n\n constructor(\n @Inject(DASHBOARD_BASE_PATH) private readonly basePath: string,\n private readonly access: DashboardConsoleAccess,\n ) {}\n\n onModuleInit(): void {\n this.#unregister = registerAviaryConsole(\n {\n id: AGENT_CONSOLE_ID,\n title: 'Agents',\n icon: 'agent',\n order: 30,\n path: this.basePath === '' ? '/' : this.basePath,\n authorize: (context) => this.access.check(context),\n },\n logger,\n );\n }\n\n onModuleDestroy(): void {\n this.#unregister?.();\n this.#unregister = undefined;\n }\n}\n\n/** What the route hands on to the other consoles' `authorize`: this request's `ExecutionContext`. */\ninterface CurrentContext {\n context: ExecutionContext;\n}\n\nconst CurrentExecutionContext = createParamDecorator(\n (_data: unknown, context: ExecutionContext): CurrentContext => ({ context }),\n);\n\n/**\n * `GET <api>/consoles`: the other Aviary consoles this viewer may open. Behind the console's own\n * access check, like every API route, so only someone who may open this console learns the list.\n */\n@CanAnyone()\n@Controller()\nexport class AgentConsolesController {\n @Get('consoles')\n @Header('Cache-Control', 'private, no-store')\n consoles(@CurrentExecutionContext() current: CurrentContext): Promise<VisibleConsoles> {\n return listVisibleConsoles(readAviaryConsoles(), AGENT_CONSOLE_ID, current.context);\n }\n}\n","import type { ExecutionContext } from '@nestjs/common';\nimport {\n Inject,\n Injectable,\n Logger,\n NotFoundException,\n type OnModuleDestroy,\n type OnModuleInit,\n} from '@nestjs/common';\nimport { DashboardConsoleAccess } from './console-authorize.guard.js';\nimport type { ConsoleDecision } from './console-authorize.js';\nimport { DashboardService } from './dashboard.service.js';\nimport { DASHBOARD_DEV_TOOLS } from './tokens.js';\n\n// ── the dev-tools contract (v1), re-declared: this package never imports the MCP server ──────\n\n/** The process-wide slot `@dudousxd/nestjs-agent-mcp-server` serves on its dev MCP endpoint. */\nconst DEV_TOOLS_SLOT = Symbol.for('@dudousxd/aviary:dev-tools');\n\ninterface DevToolInputProperty {\n type: 'string' | 'number' | 'integer' | 'boolean' | 'array';\n description?: string;\n enum?: readonly string[];\n items?: { type: 'string' };\n minimum?: number;\n maximum?: number;\n}\n\ninterface DevToolInputSchema {\n type: 'object';\n properties: Record<string, DevToolInputProperty>;\n required?: string[];\n}\n\n/** One developer tool, as the dev-tools slot takes it. */\nexport interface AgentDevTool {\n name: string;\n kind: 'read' | 'action';\n description: string;\n input: DevToolInputSchema;\n run(input: Record<string, unknown>): Promise<unknown>;\n}\n\ninterface DevToolsLib {\n lib: string;\n title: string;\n authorize(context: ExecutionContext): Promise<ConsoleDecision>;\n tools: AgentDevTool[];\n}\n\ninterface DevToolsSlot {\n readonly version: 1;\n readonly libs: DevToolsLib[];\n register(lib: DevToolsLib): () => void;\n}\n\n/** The slot, created when absent (whoever loads first creates it). */\nfunction devToolsSlot(): Partial<DevToolsSlot> {\n const holder = globalThis as Record<symbol, unknown>;\n const existing = holder[DEV_TOOLS_SLOT] as Partial<DevToolsSlot> | undefined;\n if (existing !== undefined) return existing;\n const libs: DevToolsLib[] = [];\n const slot: DevToolsSlot = {\n version: 1,\n libs,\n register(lib) {\n if (libs.some((entry) => entry.lib === lib.lib)) {\n throw new Error(`dev tools: the lib \"${lib.lib}\" is already registered`);\n }\n libs.push(lib);\n return () => {\n const index = libs.indexOf(lib);\n if (index !== -1) libs.splice(index, 1);\n };\n },\n };\n holder[DEV_TOOLS_SLOT] = slot;\n return slot;\n}\n\n// ── the agent's own tools ──────────────────────────────────────────────────────\n\nconst DEFAULT_LIMIT = 20;\nconst MAX_LIMIT = 100;\n/** Messages `agent_get_run` returns with `includeMessages: true`, each cut to 2 KB. */\nconst MESSAGE_LIMIT = 50;\nconst MESSAGE_BYTES = 2 * 1024;\nconst DAY_MS = 86_400_000;\nconst ISO_DAY = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst day: DevToolInputProperty = { type: 'string', description: 'UTC day, YYYY-MM-DD.' };\nconst page: DevToolInputProperty = {\n type: 'integer',\n description: 'Page, from 1. Pass the `next` of the previous page.',\n minimum: 1,\n};\nconst limit: DevToolInputProperty = {\n type: 'integer',\n description: `Rows per page (default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}).`,\n minimum: 1,\n maximum: MAX_LIMIT,\n};\n\nconst str = (value: unknown) => (typeof value === 'string' && value !== '' ? value : undefined);\n\nfunction dayOf(input: Record<string, unknown>, key: string): string | undefined {\n const value = str(input[key]);\n if (value !== undefined && !ISO_DAY.test(value)) throw new Error(`\"${key}\" must be YYYY-MM-DD`);\n return value;\n}\n\n/** `from`/`to`, defaulting to the last 30 days like the console. */\nfunction rangeOf(input: Record<string, unknown>) {\n const utcDay = (daysAgo: number) =>\n new Date(Date.now() - daysAgo * DAY_MS).toISOString().slice(0, 10);\n return { fromDay: dayOf(input, 'from') ?? utcDay(29), toDay: dayOf(input, 'to') ?? utcDay(0) };\n}\n\nfunction pageOf(input: Record<string, unknown>) {\n return {\n page: typeof input.page === 'number' ? input.page : 1,\n pageSize: typeof input.limit === 'number' ? input.limit : DEFAULT_LIMIT,\n };\n}\n\n/** Only the filters that were given (`exactOptionalPropertyTypes`). */\nfunction where<T extends Record<string, string | undefined>>(fields: T) {\n const set = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined));\n return Object.keys(set).length > 0 ? { where: set as { [K in keyof T]?: string } } : {};\n}\n\n/** A console page as a dev-tools list: `{ items, next, total }`. */\nfunction listOf<Row>(result: { rows: Row[]; total: number; page: number; pageSize: number }) {\n return {\n items: result.rows,\n next: result.page * result.pageSize < result.total ? result.page + 1 : null,\n total: result.total,\n };\n}\n\nfunction cut(text: string, maxBytes: number): string {\n const size = Buffer.byteLength(text, 'utf8');\n if (size <= maxBytes) return text;\n const suffix = `…(truncated, ${size} bytes)`;\n const keep = Math.max(0, maxBytes - Buffer.byteLength(suffix, 'utf8'));\n let head = Buffer.from(text, 'utf8').subarray(0, keep).toString('utf8');\n if (head.endsWith('�')) head = head.slice(0, -1);\n return `${head}${suffix}`;\n}\n\n/**\n * The agent's developer tools (lib `agent`): the governance console's reads, through the same\n * {@link DashboardService} methods its API serves. No actions: approving a tool call over MCP would\n * bypass the human approval it exists for.\n */\nexport function agentDevTools(dashboard: DashboardService): AgentDevTool[] {\n return [\n {\n name: 'agent_list_runs',\n kind: 'read',\n description:\n '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.',\n input: {\n type: 'object',\n properties: {\n agent: { type: 'string', description: 'Agent name.' },\n status: { type: 'string', enum: ['running', 'completed', 'failed', 'cancelled'] },\n errorCode: { type: 'string' },\n threadId: { type: 'string' },\n from: day,\n to: day,\n page,\n limit,\n },\n },\n async run(input) {\n return listOf(\n await dashboard.runsPage({\n ...pageOf(input),\n ...where({\n agentName: str(input.agent),\n status: str(input.status),\n errorCode: str(input.errorCode),\n threadId: str(input.threadId),\n fromDay: dayOf(input, 'from'),\n toDay: dayOf(input, 'to'),\n }),\n }),\n );\n },\n },\n {\n name: 'agent_get_run',\n kind: 'read',\n description:\n \"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.\",\n input: {\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Run id.' },\n includeMessages: { type: 'boolean', description: 'Include messages (default false).' },\n },\n required: ['id'],\n },\n async run(input) {\n let detail: Awaited<ReturnType<DashboardService['runDetail']>>;\n try {\n detail = await dashboard.runDetail(String(input.id));\n } catch (error) {\n // The endpoint answers a throw as an MCP `isError` result.\n if (error instanceof NotFoundException)\n throw new Error(`run ${String(input.id)} not found`);\n throw error;\n }\n if (input.includeMessages !== true) return detail;\n const thread = await dashboard.threadDetail({\n threadId: detail.run.threadId,\n messageLimit: MESSAGE_LIMIT,\n runLimit: 1,\n });\n return {\n ...detail,\n messages: thread.messages.map((message) => ({\n ...message,\n content: cut(message.content, MESSAGE_BYTES),\n })),\n };\n },\n },\n {\n name: 'agent_get_spend',\n kind: 'read',\n description:\n '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.',\n input: {\n type: 'object',\n properties: {\n by: { type: 'string', enum: ['model', 'actor', 'feature'] },\n from: day,\n to: day,\n },\n },\n async run(input) {\n const spend = await dashboard.spend(rangeOf(input));\n switch (input.by) {\n case 'model':\n return { items: spend.byModel };\n case 'actor':\n return { items: spend.byActor };\n case 'feature':\n return { items: spend.byFeature ?? [] };\n default:\n return spend;\n }\n },\n },\n {\n name: 'agent_get_tool_stats',\n kind: 'read',\n description:\n 'Per tool: calls, failures, rejections and latency, over UTC days from/to (default the last 30 days).',\n input: { type: 'object', properties: { from: day, to: day } },\n async run(input) {\n return { items: await dashboard.toolStats(rangeOf(input)) };\n },\n },\n {\n name: 'agent_get_reliability',\n kind: 'read',\n description:\n 'Run metrics, by-agent and by-error breakdowns and the trend, over UTC days from/to (default the last 30 days).',\n input: { type: 'object', properties: { from: day, to: day } },\n async run(input) {\n return dashboard.reliability(rangeOf(input));\n },\n },\n {\n name: 'agent_list_pending_approvals',\n kind: 'read',\n description:\n 'Tool calls waiting for a human approval, oldest first. Read-only: approve them in the app. Returns { items, next, total }.',\n input: {\n type: 'object',\n properties: {\n toolName: { type: 'string' },\n agent: { type: 'string', description: 'Agent name.' },\n actor: { type: 'string', description: 'Actor ref of the thread owner.' },\n threadId: { type: 'string' },\n from: day,\n to: day,\n page,\n limit,\n },\n },\n async run(input) {\n return listOf(\n await dashboard.approvalsPage({\n ...pageOf(input),\n ...where({\n toolName: str(input.toolName),\n agentName: str(input.agent),\n actorRef: str(input.actor),\n threadId: str(input.threadId),\n fromDay: dayOf(input, 'from'),\n toDay: dayOf(input, 'to'),\n }),\n }),\n );\n },\n },\n {\n name: 'agent_list_recent_tool_calls',\n kind: 'read',\n description:\n 'Tool calls of every agent, newest first. Filters: toolName, status, threadId, from/to (UTC days). Returns { items, next, total }.',\n input: {\n type: 'object',\n properties: {\n toolName: { type: 'string' },\n status: { type: 'string' },\n threadId: { type: 'string' },\n from: day,\n to: day,\n page,\n limit,\n },\n },\n async run(input) {\n return listOf(\n await dashboard.toolCallsPage({\n ...pageOf(input),\n ...where({\n toolName: str(input.toolName),\n status: str(input.status),\n threadId: str(input.threadId),\n fromDay: dayOf(input, 'from'),\n toDay: dayOf(input, 'to'),\n }),\n }),\n );\n },\n },\n ];\n}\n\nconst logger = new Logger('AgentDashboardModule');\n\n/**\n * Registers the agent's tools into the dev-tools slot, gated by the console's own resolved\n * `authorize`, when the console is mounted (or `devTools: true`). Removed again on shutdown.\n */\n@Injectable()\nexport class AgentDevToolsRegistration implements OnModuleInit, OnModuleDestroy {\n #unregister: (() => void) | undefined;\n\n constructor(\n @Inject(DASHBOARD_DEV_TOOLS) private readonly enabled: boolean,\n private readonly dashboard: DashboardService,\n private readonly access: DashboardConsoleAccess,\n ) {}\n\n onModuleInit(): void {\n if (!this.enabled) return;\n const slot = devToolsSlot();\n if (slot.version !== 1 || typeof slot.register !== 'function') return;\n try {\n this.#unregister = slot.register({\n lib: 'agent',\n title: 'Agent governance',\n authorize: (context) => this.access.check(context),\n tools: agentDevTools(this.dashboard),\n });\n } catch (error) {\n // Another application in this process registered the agent's tools: keep the console up.\n logger.warn(\n `agent dev tools not registered: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n onModuleDestroy(): void {\n this.#unregister?.();\n this.#unregister = undefined;\n }\n}\n"],"mappings":"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","names":["CanAnyone","Body","Controller","Get","HttpCode","Inject","Optional","Param","Post","Query","Req","Sse","subscribe","unsubscribe","channelName","Inject","Injectable","NotFoundException","NotImplementedException","Optional","Observable","BadRequestException","parseApprovalDecision","body","BadRequestException","approved","reason","undefined","BadRequestException","parsePriceInput","body","BadRequestException","modelId","inputPricePer1m","outputPricePer1m","cacheWritePricePer1m","cacheReadPricePer1m","trim","length","isFiniteNonNegative","undefined","value","Number","isFinite","AGENT_GOVERNANCE_QUERIES","Symbol","for","AGENT_ACTOR_DIRECTORY","AGENT_PRICING_STORE","AGENT_APPROVAL_PORT","AGENT_ACTOR_RESOLVER","DASHBOARD_BASE_PATH","DASHBOARD_API_PATH","DASHBOARD_APPROVAL_ACTOR_REF","DASHBOARD_CONSOLE_OPTIONS","DASHBOARD_DEV_TOOLS","PRICING_STORE_UNBOUND_MESSAGE","APPROVAL_PORT_UNBOUND_MESSAGE","GENUI_STATS_UNSUPPORTED_MESSAGE","AGENT_EVENTS","isAgentEnvelope","message","event","DashboardService","queries","actorDirectory","pricingStore","approvalPort","spend","range","byModel","byActorRaw","trend","byFeature","Promise","all","spendByModel","spendByActor","usageTrend","spendByFeature","byActor","withActorLabels","undefined","topThreads","limit","rows","spendByThread","reliability","metrics","byAgent","errors","runMetrics","runsByAgent","runErrors","runTrend","recentRuns","recentToolCalls","toolCallsPage","query","runsPage","pendingApprovals","approvalsPage","runDetail","runId","detail","NotFoundException","thread","actorLabel","threadDetail","threadId","toolStats","genuiStats","NotImplementedException","decideApproval","toolCallId","body","executedByRef","decision","parseApprovalDecision","opts","approved","approve","reject","reason","recentThreads","threadsPage","page","length","map","row","refs","Set","actorRef","resolved","resolveDisplay","listPrices","listCurrentPrices","upsertPrice","upsertModelPrice","parsePriceInput","streamEvents","Observable","subscriber","bindings","name","channelName","handler","next","data","ts","Date","now","payload","subscribe","binding","unsubscribe","BadRequestException","ISO_DAY","parsePageNumber","value","parsed","undefined","Number","NaN","parseInt","isFinite","assertKnownFields","raw","allowed","field","Object","keys","includes","BadRequestException","assertDayFields","dayFields","test","DAY_FIELDS","TOOL_CALL_WHERE_FIELDS","parseToolCallWhere","length","toolName","toolType","status","threadId","fromDay","toDay","THREAD_WHERE_FIELDS","parseThreadWhere","actorRef","title","RUN_WHERE_FIELDS","parseRunWhere","agentName","errorCode","APPROVAL_WHERE_FIELDS","parseApprovalWhere","DAY_MS","ISO_DAY","utcDay","daysAgo","Date","now","toISOString","slice","dayOr","value","fallback","undefined","test","resolveRange","from","to","fromDay","toDay","parseLimit","parsed","Number","NaN","parseInt","isFinite","Math","max","min","AgentApiController","dashboard","approvalActorRef","actorResolver","spend","topThreads","limit","reliability","runs","recentRuns","runsPage","page","where","parsedWhere","parseRunWhere","parsePageNumber","pageSize","runDetail","runId","toolCalls","recentToolCalls","toolCallsPage","parseToolCallWhere","approvals","pendingApprovals","approvalsPage","parseApprovalWhere","decideApproval","toolCallId","body","req","deciderRef","resolve","id","tools","toolStats","genui","genuiStats","threads","recentThreads","threadsPage","parseThreadWhere","threadDetail","threadId","messages","messageLimit","runLimit","listPrices","upsertPrice","stream","streamEvents","normalizeDashboardPath","path","replace","unprefixed","path","replace","agentDashboardMountPaths","options","basePath","normalizeDashboardPath","apiBasePath","base","api","withControllerDecorators","Module","UseFilters","UseGuards","RouterModule","existsSync","readFileSync","basename","extname","join","resolve","sep","fileURLToPath","CanAnyone","Controller","Get","Header","Inject","NotFoundException","Param","Req","Res","StreamableFile","Catch","HttpException","Inject","Injectable","SetMetadata","UseFilters","UseGuards","applyDecorators","HttpAdapterHost","ModuleRef","createHash","timingSafeEqual","Logger","ModuleRef","CONSOLE_GATE","Symbol","for","PACKAGE","MODULE","logger","Logger","CONSOLE_AUTHORIZE_MISSING_WARNING","allow","caller","undefined","allowed","forbidden","reason","unauthenticated","isProduction","process","env","NODE_ENV","trim","toLowerCase","normalizeConsoleResult","result","value","redirectTo","isAuthorizerClass","prototype","authorize","test","Function","toString","call","isPolicyCheck","policy","ability","lazyModuleClass","candidate","default","tryGet","moduleRef","token","get","strict","classInstances","instances","Map","Class","instance","registered","Promise","resolve","create","catch","delete","set","hasUser","user","policyDecider","check","mod","Error","service","AuthorizationService","options","AUTHORIZATION_MODULE_OPTIONS","getUser","defaultGetUser","context","signedIn","can","resolveConsoleAuthorize","log","warnIfMissing","warn","production","instanceOf","decide","name","gate","allows","fn","lazyClass","error","message","String","sha256","createHash","update","digest","authorizeByToken","expected","slice","req","switchToHttp","getRequest","header","headers","authorization","Array","isArray","match","exec","timingSafeEqual","anyAuthorize","forms","length","AnyConsoleAuthorizer","all","map","form","decision","Reflect","defineMetadata","ModuleRef","DashboardConsoleAccess","options","moduleRef","onModuleInit","resolveConsoleAuthorize","authorize","check","context","undefined","CONSOLE_PAGE_METADATA","ConsolePage","SetMetadata","ConsoleAccessDenied","HttpException","decision","page","reason","error","redirectTo","isPageNavigation","getType","Reflect","getMetadata","getHandler","getClass","req","switchToHttp","getRequest","method","toUpperCase","accept","headers","value","Array","isArray","join","includes","ConsoleAuthorizeGuard","access","canActivate","allowed","deniedPage","title","message","body","FORBIDDEN_PAGE","UNAUTHENTICATED_PAGE","ConsoleAuthorizeFilter","adapterHost","catch","exception","host","adapter","httpAdapter","res","getResponse","redirect","forbidden","setHeader","reply","getStatus","UseConsoleAuthorize","applyDecorators","UseGuards","UseFilters","CanAnyone","Controller","Get","Header","Inject","Injectable","Logger","createParamDecorator","AVIARY_CONSOLES_SLOT","Symbol","for","ID","ICONS","Set","CONSOLE_PROBE_TIMEOUT_MS","isSameOriginPath","path","startsWith","readAviaryConsoles","value","globalThis","version","Array","isArray","entries","undefined","registerAviaryConsole","entry","logger","test","id","Error","JSON","stringify","holder","fresh","registry","some","existing","warn","push","index","indexOf","splice","consoleSwitcherConfig","selfId","endpoint","others","filter","length","allows","context","ms","timer","timeout","Promise","resolve","setTimeout","race","then","authorize","decision","allowed","clearTimeout","listVisibleConsoles","timeoutMs","sort","a","b","order","localeCompare","verdicts","all","map","current","consoles","_","title","icon","has","href","AGENT_CONSOLE_ID","Logger","AgentConsoleRegistration","basePath","access","onModuleInit","check","onModuleDestroy","CurrentExecutionContext","createParamDecorator","_data","AgentConsolesController","BUILD_BASE","AGENT_DASHBOARD_CONFIG_ID","spaDir","fileURLToPath","URL","url","CONTENT_TYPES","AgentUiController","dir","basePath","apiBasePath","index","req","res","headers","undefined","status","setHeader","originalUrl","indexPath","join","existsSync","NotFoundException","html","readFileSync","replaceAll","inject","configBlock","includes","replace","switcher","consoleSwitcherConfig","AGENT_CONSOLE_ID","json","JSON","stringify","apiBase","asset","file","safe","basename","root","resolve","assetPath","startsWith","sep","type","extname","StreamableFile","passthrough","Inject","Injectable","Logger","NotFoundException","DEV_TOOLS_SLOT","Symbol","for","devToolsSlot","holder","globalThis","existing","undefined","libs","slot","version","register","lib","some","entry","Error","push","index","indexOf","splice","DEFAULT_LIMIT","MAX_LIMIT","MESSAGE_LIMIT","MESSAGE_BYTES","DAY_MS","ISO_DAY","day","type","description","page","minimum","limit","maximum","str","value","dayOf","input","key","test","rangeOf","utcDay","daysAgo","Date","now","toISOString","slice","fromDay","toDay","pageOf","pageSize","where","fields","set","Object","fromEntries","entries","filter","v","keys","length","listOf","result","items","rows","next","total","cut","text","maxBytes","size","Buffer","byteLength","suffix","keep","Math","max","head","from","subarray","toString","endsWith","agentDevTools","dashboard","name","kind","properties","agent","status","enum","errorCode","threadId","to","run","runsPage","agentName","id","includeMessages","required","detail","runDetail","String","error","NotFoundException","thread","threadDetail","messageLimit","runLimit","messages","map","message","content","by","spend","byModel","byActor","byFeature","toolStats","reliability","toolName","actor","approvalsPage","actorRef","toolCallsPage","logger","Logger","AgentDevToolsRegistration","enabled","access","onModuleInit","title","authorize","context","check","tools","warn","onModuleDestroy","normalize","path","normalizeDashboardPath","consoleDecorators","decorators","UseGuards","ConsoleAuthorizeGuard","UseFilters","ConsoleAuthorizeFilter","AgentApiModule","register","options","consoleOptions","authorize","undefined","module","imports","controllers","routes","withControllerDecorators","AgentApiController","AgentConsolesController","providers","DashboardService","provide","DASHBOARD_APPROVAL_ACTOR_REF","useValue","approvalActorRef","DASHBOARD_CONSOLE_OPTIONS","DashboardConsoleAccess","DASHBOARD_DEV_TOOLS","devTools","AgentDevToolsRegistration","exports","AgentDashboardModule","forRoot","basePath","apiBasePath","RouterModule","AgentUiController","DASHBOARD_BASE_PATH","DASHBOARD_API_PATH","AgentConsoleRegistration"]}
|