@dudousxd/nestjs-agent-dashboard 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { ActorSpendRow, AgentGovernanceQueries, ActorDirectory, AgentPricingStore, AgentApprovalPort, GovernanceRange, ModelSpendRow, UsageTrendPoint, ThreadSpendRow, RunMetrics, RunAgentBreakdownRow, RunErrorBreakdownRow, RunTrendPoint, RecentRunRow, ToolCallActivityRow, PendingApprovalRow, ToolStatRow, ThreadActivityRow, CurrentModelPrice, ModelPriceInput } from '@dudousxd/nestjs-agent-core';
1
+ import { ActorSpendRow, AgentGovernanceQueries, ActorDirectory, AgentPricingStore, AgentApprovalPort, GovernanceRange, ModelSpendRow, UsageTrendPoint, ThreadSpendRow, RunMetrics, RunAgentBreakdownRow, RunErrorBreakdownRow, RunTrendPoint, RecentRunRow, ToolCallActivityRow, GovernancePageQuery, ToolCallWhere, GovernancePage, RunWhere, PendingApprovalRow, ToolStatRow, ThreadActivityRow, ThreadWhere, CurrentModelPrice, ActorResolver, ModelPriceInput } from '@dudousxd/nestjs-agent-core';
2
2
  export { ActorDirectory } from '@dudousxd/nestjs-agent-core';
3
3
  import { Observable } from 'rxjs';
4
4
  import { DynamicModule, Type, CanActivate, StreamableFile } from '@nestjs/common';
@@ -61,6 +61,10 @@ declare class DashboardService {
61
61
  recentRuns(limit: number): Promise<RecentRunRow[]>;
62
62
  /** Most recent tool calls (status/type/thread) for the Runs & tools activity feed. */
63
63
  recentToolCalls(limit: number): Promise<ToolCallActivityRow[]>;
64
+ /** Paged, filterable tool calls for the Runs & tools activity table. */
65
+ toolCallsPage(query: GovernancePageQuery<ToolCallWhere>): Promise<GovernancePage<ToolCallActivityRow>>;
66
+ /** Paged, filterable runs for the Reliability recent-runs table. */
67
+ runsPage(query: GovernancePageQuery<RunWhere>): Promise<GovernancePage<RecentRunRow>>;
64
68
  /** Tool calls sitting `pending_approval`, oldest first, for the cross-thread approvals inbox. */
65
69
  pendingApprovals(limit: number): Promise<PendingApprovalRow[]>;
66
70
  /** Per-tool call/failure/rejection/latency rollup for a day range, for the Tools section. */
@@ -76,6 +80,11 @@ declare class DashboardService {
76
80
  decideApproval(toolCallId: string, body: unknown, executedByRef: string | undefined): Promise<void>;
77
81
  /** Most recent threads with rolled-up message/token counts. */
78
82
  recentThreads(limit: number): Promise<ThreadActivityRowWithLabel[]>;
83
+ /**
84
+ * Paged, filterable threads for the Runs & tools threads table — actor-labeled the same way
85
+ * {@link recentThreads} is.
86
+ */
87
+ threadsPage(query: GovernancePageQuery<ThreadWhere>): Promise<GovernancePage<ThreadActivityRowWithLabel>>;
79
88
  /**
80
89
  * Decorate actor-scoped rows with `actorLabel`, batching the distinct `actorRef`s into ONE
81
90
  * {@link ActorDirectory.resolveDisplay} call per response. `null` for every row when no directory
@@ -107,7 +116,8 @@ declare class DashboardService {
107
116
  declare class AgentApiController {
108
117
  private readonly dashboard;
109
118
  private readonly approvalActorRef;
110
- constructor(dashboard: DashboardService, approvalActorRef: ((req: unknown) => string | undefined) | undefined);
119
+ private readonly actorResolver?;
120
+ constructor(dashboard: DashboardService, approvalActorRef: ((req: unknown) => string | undefined) | undefined, actorResolver?: ActorResolver | undefined);
111
121
  /** `{ byModel, byActor, trend }` for a day range (defaults to the last 30 days). */
112
122
  spend(from?: string, to?: string): Promise<SpendOverview>;
113
123
  /** Top threads by cost (default 10, max 200) for a day range (defaults to the last 30 days). */
@@ -116,20 +126,46 @@ declare class AgentApiController {
116
126
  reliability(from?: string, to?: string): Promise<ReliabilityOverview>;
117
127
  /** Most recent runs (default 50, max 200) for the Reliability recent-runs table. */
118
128
  runs(limit?: string): Promise<RecentRunRow[]>;
129
+ /**
130
+ * Paged, filterable runs for the Reliability recent-runs table. `page` (default 1), `limit`
131
+ * (default 25, max 200), and `where[agentName]`/`where[status]`/`where[errorCode]`/
132
+ * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
133
+ */
134
+ runsPage(page?: string, limit?: string, where?: Record<string, string>): Promise<GovernancePage<RecentRunRow>>;
119
135
  /** Most recent tool calls (default 50, max 200) for the activity feed. */
120
136
  toolCalls(limit?: string): Promise<ToolCallActivityRow[]>;
137
+ /**
138
+ * Paged, filterable tool calls for the Runs & tools activity table. `page` (default 1), `limit`
139
+ * (default 25, max 200), and `where[toolName]`/`where[toolType]`/`where[status]`/`where[threadId]`/
140
+ * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
141
+ */
142
+ toolCallsPage(page?: string, limit?: string, where?: Record<string, string>): Promise<GovernancePage<ToolCallActivityRow>>;
121
143
  /** Tool calls sitting `pending_approval` (default 50, max 200), oldest first — the approvals inbox. */
122
144
  approvals(limit?: string): Promise<PendingApprovalRow[]>;
123
145
  /**
124
146
  * Decide a pending HITL tool call. Body `{ approved: boolean; reason?: string }`. 501s (via
125
147
  * `DashboardService.decideApproval`) when no `AGENT_APPROVAL_PORT` is bound. `executedByRef`
126
- * comes from the host's `approvalActorRef` extractor run against the live request, when configured.
148
+ * comes from {@link deciderRef} run against the live request.
127
149
  */
128
150
  decideApproval(toolCallId: string, body: unknown, req: unknown): Promise<void>;
151
+ /**
152
+ * WHO decided, as an opaque ref: an explicit `approvalActorRef` override wins outright (no
153
+ * resolver fallback, even when it returns `undefined`); otherwise the AgentModule-configured
154
+ * actor resolver — the same identity seam chat requests use. A resolver that throws is an
155
+ * unauthenticated/unreadable request, not an error to surface: the decision itself was already
156
+ * authorized by the dashboard's guards, so the ref is simply omitted.
157
+ */
158
+ private deciderRef;
129
159
  /** Per-tool call/failure/rejection/latency rollup for a day range (defaults to the last 30 days). */
130
160
  tools(from?: string, to?: string): Promise<ToolStatRow[]>;
131
161
  /** Most recent threads (default 50, max 200) with rolled-up counts. */
132
162
  threads(limit?: string): Promise<ThreadActivityRowWithLabel[]>;
163
+ /**
164
+ * Paged, filterable threads for the Runs & tools threads table. `page` (default 1), `limit`
165
+ * (default 25, max 200), and `where[actorRef]`/`where[title]` (case-insensitive substring)/
166
+ * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
167
+ */
168
+ threadsPage(page?: string, limit?: string, where?: Record<string, string>): Promise<GovernancePage<ThreadActivityRowWithLabel>>;
133
169
  /**
134
170
  * Current price row per model, for the pricing tab. 501s (via `DashboardService.listPrices`) when
135
171
  * no `AGENT_PRICING_STORE` is bound.
@@ -178,7 +214,13 @@ interface AgentDashboardMountPathsOptions {
178
214
  */
179
215
  declare function agentDashboardMountPaths(options?: AgentDashboardMountPathsOptions): string[];
180
216
 
181
- interface AgentDashboardOptions {
217
+ /**
218
+ * `TReq` mirrors core's `ActorResolver<TReq>` convention: the transport request type
219
+ * {@link AgentDashboardOptions.approvalActorRef} receives. Defaults to `unknown` so untyped usage
220
+ * compiles unchanged; a host may narrow it (e.g. `forRoot<Request>({ approvalActorRef: ... })`)
221
+ * instead of writing its own `unknown`-narrowing type guard.
222
+ */
223
+ interface AgentDashboardOptions<TReq = unknown> {
182
224
  /**
183
225
  * Where the SPA (UI) is served. Default `/ai-gateway`. This is a page route — keep it out of an
184
226
  * `/api` prefix so it reads as a UI, not an endpoint.
@@ -208,13 +250,17 @@ interface AgentDashboardOptions {
208
250
  */
209
251
  imports?: DynamicModule['imports'];
210
252
  /**
211
- * Extracts WHO is deciding a HITL approval from the live request — invoked on the incoming
253
+ * OVERRIDE for WHO is deciding a HITL approval — invoked on the incoming
212
254
  * `POST <api>/approvals/:toolCallId` request to stamp `AgentApprovalPort`'s `opts.executedByRef`.
213
- * Typically pulls the authenticated principal your own auth guard/middleware already attached to
214
- * the request (e.g. `(req) => (req as { user?: { id: string } }).user?.id`). Omit to leave
215
- * `executedByRef` unset — the decision still goes through, just without a recorded decider ref.
255
+ *
256
+ * DEFAULT (option omitted): the AgentModule-configured actor resolver (`AGENT_ACTOR_RESOLVER`) is
257
+ * consulted — the same identity seam chat requests use and its actor id becomes the decider ref
258
+ * (a throwing resolver, i.e. an unauthenticated request, just omits the ref). Set this only when
259
+ * console auth differs from chat auth (e.g. the console sits behind a separate SSO whose principal
260
+ * the chat resolver can't read); returning `undefined` leaves `executedByRef` unset — the explicit
261
+ * override wins outright, with no resolver fallback.
216
262
  */
217
- approvalActorRef?: (req: unknown) => string | undefined;
263
+ approvalActorRef?: (req: TReq) => string | undefined;
218
264
  }
219
265
  /**
220
266
  * Holds the JSON API + SSE controller and its read service, mounted on its own path by `forRoot`.
@@ -225,10 +271,10 @@ interface AgentDashboardOptions {
225
271
  * the right `imports` to `forRoot`.
226
272
  */
227
273
  declare class AgentApiModule {
228
- static register(options: {
274
+ static register<TReq = unknown>(options: {
229
275
  imports?: DynamicModule['imports'];
230
276
  guards?: Type<CanActivate>[];
231
- approvalActorRef?: (req: unknown) => string | undefined;
277
+ approvalActorRef?: (req: TReq) => string | undefined;
232
278
  }): DynamicModule;
233
279
  }
234
280
  /**
@@ -239,11 +285,31 @@ declare class AgentApiModule {
239
285
  * (global), which must provide `AGENT_GOVERNANCE_QUERIES` (bound by a store adapter). Front the
240
286
  * routes with the first-class `guards` option (plus `imports` for the guards' own dependencies) —
241
287
  * see {@link AgentDashboardOptions.guards}.
288
+ *
289
+ * Inertia hosts: the console is a full-page app, not an Inertia page. An in-app `<Link>` visit to
290
+ * `basePath` (an XHR carrying `X-Inertia`) is bounced with the protocol's own external-redirect
291
+ * mechanism — `409 Conflict` + `X-Inertia-Location: <the visited URL>` — so the Inertia client
292
+ * performs a full `window.location` load and the console renders normally. In-app links to the
293
+ * console therefore just work; no host-side special-casing needed.
242
294
  */
243
295
  declare class AgentDashboardModule {
244
- static forRoot(options?: AgentDashboardOptions): DynamicModule;
296
+ static forRoot<TReq = unknown>(options?: AgentDashboardOptions<TReq>): DynamicModule;
245
297
  }
246
298
 
299
+ /**
300
+ * The slice of the express request the Inertia bounce below reads — structural, so the package
301
+ * needs no express dependency (the API controller similarly takes `@Req() req: unknown`).
302
+ */
303
+ interface UiPageRequest {
304
+ headers: Record<string, string | string[] | undefined>;
305
+ /** Path + query as received (express `originalUrl`) — what the full-page visit must reload. */
306
+ originalUrl: string;
307
+ }
308
+ /** The slice of the express response the Inertia bounce writes (passthrough mode — Nest still sends). */
309
+ interface UiPageResponse {
310
+ status(code: number): unknown;
311
+ setHeader(name: string, value: string): unknown;
312
+ }
247
313
  /**
248
314
  * Serves the bundled AI-gateway console SPA at the configured base (+ hashed assets at
249
315
  * `<base>/assets`). The path comes from `RouterModule` (set by
@@ -254,7 +320,7 @@ declare class AgentUiController {
254
320
  private readonly apiBasePath;
255
321
  private readonly dir;
256
322
  constructor(basePath: string, apiBasePath: string);
257
- index(): string;
323
+ index(req: UiPageRequest, res: UiPageResponse): string;
258
324
  asset(file: string): StreamableFile;
259
325
  }
260
326
 
@@ -301,16 +367,26 @@ declare const AGENT_PRICING_STORE: unique symbol;
301
367
  * absent, `POST <api>/approvals/:toolCallId` 501s and the approvals inbox renders read-only.
302
368
  */
303
369
  declare const AGENT_APPROVAL_PORT: unique symbol;
370
+ /**
371
+ * The app's request-identity seam (`ActorResolver`), owned by `@dudousxd/nestjs-agent-core` and
372
+ * bound + exported by the (global) `AgentModule` from the host's `actorResolver` option. Re-declared
373
+ * here BY VALUE for the same reason as {@link AGENT_GOVERNANCE_QUERIES} above. Optional: the default
374
+ * decider attribution for `POST <api>/approvals/:toolCallId` — absent (or throwing, i.e.
375
+ * unauthenticated), `executedByRef` is simply omitted.
376
+ */
377
+ declare const AGENT_ACTOR_RESOLVER: unique symbol;
304
378
  /** DI token carrying the UI mount base (e.g. `/ai-gateway`). */
305
379
  declare const DASHBOARD_BASE_PATH: unique symbol;
306
380
  /** DI token carrying the JSON API base the SPA fetches from (e.g. `/ai-gateway/api`). */
307
381
  declare const DASHBOARD_API_PATH: unique symbol;
308
382
  /**
309
383
  * DI token carrying the host-provided {@link AgentDashboardOptions.approvalActorRef} extractor (or
310
- * `undefined` when the host didn't set one). Threaded to `AgentApiModule` (where the API controller
311
- * actually lives) same as the pattern above `useValue` even when `undefined`, so injecting it
312
- * needs no `@Optional()` (mirrors `AGENT_QUOTA_STORE`'s factory in `@dudousxd/nestjs-agent`).
384
+ * `undefined` when the host didn't set one the controller then falls back to
385
+ * {@link AGENT_ACTOR_RESOLVER} for decider attribution). Threaded to `AgentApiModule` (where the API
386
+ * controller actually lives) same as the pattern above — `useValue` even when `undefined`, so
387
+ * injecting it needs no `@Optional()` (mirrors `AGENT_QUOTA_STORE`'s factory in
388
+ * `@dudousxd/nestjs-agent`).
313
389
  */
314
390
  declare const DASHBOARD_APPROVAL_ACTOR_REF: unique symbol;
315
391
 
316
- export { AGENT_ACTOR_DIRECTORY, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, AgentUiController, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_BASE_PATH, DashboardService, type LiveAgentEvent, type ReliabilityOverview, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadSpendRowWithLabel, type WithActorLabel, agentDashboardMountPaths, parsePriceInput };
392
+ export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_GOVERNANCE_QUERIES, AGENT_PRICING_STORE, type ActorSpendRowWithLabel, AgentApiController, AgentApiModule, AgentDashboardModule, type AgentDashboardMountPathsOptions, type AgentDashboardOptions, AgentUiController, DASHBOARD_API_PATH, DASHBOARD_APPROVAL_ACTOR_REF, DASHBOARD_BASE_PATH, DashboardService, type LiveAgentEvent, type ReliabilityOverview, type SpendOverview, type ThreadActivityRowWithLabel, type ThreadSpendRowWithLabel, type WithActorLabel, agentDashboardMountPaths, parsePriceInput };
@@ -2,7 +2,7 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/server/agent-api.controller.ts
5
- import { Body, Controller, Get, HttpCode, Inject as Inject2, Param, Post, Query, Req, Sse } from "@nestjs/common";
5
+ import { Body, Controller, Get, HttpCode, Inject as Inject2, Optional as Optional2, Param, Post, Query, Req, Sse } from "@nestjs/common";
6
6
 
7
7
  // src/server/dashboard.service.ts
8
8
  import { subscribe, unsubscribe } from "node:diagnostics_channel";
@@ -77,6 +77,7 @@ var AGENT_GOVERNANCE_QUERIES = Symbol.for("@dudousxd/nestjs-agent:governance-que
77
77
  var AGENT_ACTOR_DIRECTORY = Symbol.for("@dudousxd/nestjs-agent:actor-directory");
78
78
  var AGENT_PRICING_STORE = Symbol.for("@dudousxd/nestjs-agent:pricing-store");
79
79
  var AGENT_APPROVAL_PORT = Symbol.for("@dudousxd/nestjs-agent:approval-port");
80
+ var AGENT_ACTOR_RESOLVER = Symbol.for("@dudousxd/nestjs-agent:actor-resolver");
80
81
  var DASHBOARD_BASE_PATH = Symbol.for("@dudousxd/nestjs-agent-dashboard:base-path");
81
82
  var DASHBOARD_API_PATH = Symbol.for("@dudousxd/nestjs-agent-dashboard:api-path");
82
83
  var DASHBOARD_APPROVAL_ACTOR_REF = Symbol.for("@dudousxd/nestjs-agent-dashboard:approval-actor-ref");
@@ -169,6 +170,14 @@ var DashboardService = class {
169
170
  recentToolCalls(limit) {
170
171
  return this.queries.recentToolCalls(limit);
171
172
  }
173
+ /** Paged, filterable tool calls for the Runs & tools activity table. */
174
+ toolCallsPage(query) {
175
+ return this.queries.toolCallsPage(query);
176
+ }
177
+ /** Paged, filterable runs for the Reliability recent-runs table. */
178
+ runsPage(query) {
179
+ return this.queries.runsPage(query);
180
+ }
172
181
  /** Tool calls sitting `pending_approval`, oldest first, for the cross-thread approvals inbox. */
173
182
  pendingApprovals(limit) {
174
183
  return this.queries.pendingApprovals(limit);
@@ -210,6 +219,18 @@ var DashboardService = class {
210
219
  return this.withActorLabels(rows);
211
220
  }
212
221
  /**
222
+ * Paged, filterable threads for the Runs & tools threads table — actor-labeled the same way
223
+ * {@link recentThreads} is.
224
+ */
225
+ async threadsPage(query) {
226
+ const page = await this.queries.threadsPage(query);
227
+ const rows = await this.withActorLabels(page.rows);
228
+ return {
229
+ ...page,
230
+ rows
231
+ };
232
+ }
233
+ /**
213
234
  * Decorate actor-scoped rows with `actorLabel`, batching the distinct `actorRef`s into ONE
214
235
  * {@link ActorDirectory.resolveDisplay} call per response. `null` for every row when no directory
215
236
  * is bound, or for a ref the directory didn't resolve.
@@ -300,6 +321,127 @@ DashboardService = _ts_decorate([
300
321
  ])
301
322
  ], DashboardService);
302
323
 
324
+ // src/server/parse-page-query.ts
325
+ import { BadRequestException as BadRequestException3 } from "@nestjs/common";
326
+ var ISO_DAY = /^\d{4}-\d{2}-\d{2}$/;
327
+ function parsePageNumber(value) {
328
+ const parsed = value === void 0 ? Number.NaN : Number.parseInt(value, 10);
329
+ if (!Number.isFinite(parsed) || parsed < 1) return 1;
330
+ return parsed;
331
+ }
332
+ __name(parsePageNumber, "parsePageNumber");
333
+ function assertKnownFields(raw, allowed) {
334
+ for (const field of Object.keys(raw)) {
335
+ if (!allowed.includes(field)) {
336
+ throw new BadRequestException3(`Unknown where field "${field}".`);
337
+ }
338
+ }
339
+ }
340
+ __name(assertKnownFields, "assertKnownFields");
341
+ function assertDayFields(raw, dayFields) {
342
+ for (const field of dayFields) {
343
+ const value = raw[field];
344
+ if (value !== void 0 && !ISO_DAY.test(value)) {
345
+ throw new BadRequestException3(`"where[${field}]" must be YYYY-MM-DD.`);
346
+ }
347
+ }
348
+ }
349
+ __name(assertDayFields, "assertDayFields");
350
+ var DAY_FIELDS = [
351
+ "fromDay",
352
+ "toDay"
353
+ ];
354
+ var TOOL_CALL_WHERE_FIELDS = [
355
+ "toolName",
356
+ "toolType",
357
+ "status",
358
+ "threadId",
359
+ "fromDay",
360
+ "toDay"
361
+ ];
362
+ function parseToolCallWhere(raw) {
363
+ if (raw === void 0 || Object.keys(raw).length === 0) return void 0;
364
+ assertKnownFields(raw, TOOL_CALL_WHERE_FIELDS);
365
+ assertDayFields(raw, DAY_FIELDS);
366
+ return {
367
+ ...raw.toolName !== void 0 ? {
368
+ toolName: raw.toolName
369
+ } : {},
370
+ ...raw.toolType !== void 0 ? {
371
+ toolType: raw.toolType
372
+ } : {},
373
+ ...raw.status !== void 0 ? {
374
+ status: raw.status
375
+ } : {},
376
+ ...raw.threadId !== void 0 ? {
377
+ threadId: raw.threadId
378
+ } : {},
379
+ ...raw.fromDay !== void 0 ? {
380
+ fromDay: raw.fromDay
381
+ } : {},
382
+ ...raw.toDay !== void 0 ? {
383
+ toDay: raw.toDay
384
+ } : {}
385
+ };
386
+ }
387
+ __name(parseToolCallWhere, "parseToolCallWhere");
388
+ var THREAD_WHERE_FIELDS = [
389
+ "actorRef",
390
+ "title",
391
+ "fromDay",
392
+ "toDay"
393
+ ];
394
+ function parseThreadWhere(raw) {
395
+ if (raw === void 0 || Object.keys(raw).length === 0) return void 0;
396
+ assertKnownFields(raw, THREAD_WHERE_FIELDS);
397
+ assertDayFields(raw, DAY_FIELDS);
398
+ return {
399
+ ...raw.actorRef !== void 0 ? {
400
+ actorRef: raw.actorRef
401
+ } : {},
402
+ ...raw.title !== void 0 ? {
403
+ title: raw.title
404
+ } : {},
405
+ ...raw.fromDay !== void 0 ? {
406
+ fromDay: raw.fromDay
407
+ } : {},
408
+ ...raw.toDay !== void 0 ? {
409
+ toDay: raw.toDay
410
+ } : {}
411
+ };
412
+ }
413
+ __name(parseThreadWhere, "parseThreadWhere");
414
+ var RUN_WHERE_FIELDS = [
415
+ "agentName",
416
+ "status",
417
+ "errorCode",
418
+ "fromDay",
419
+ "toDay"
420
+ ];
421
+ function parseRunWhere(raw) {
422
+ if (raw === void 0 || Object.keys(raw).length === 0) return void 0;
423
+ assertKnownFields(raw, RUN_WHERE_FIELDS);
424
+ assertDayFields(raw, DAY_FIELDS);
425
+ return {
426
+ ...raw.agentName !== void 0 ? {
427
+ agentName: raw.agentName
428
+ } : {},
429
+ ...raw.status !== void 0 ? {
430
+ status: raw.status
431
+ } : {},
432
+ ...raw.errorCode !== void 0 ? {
433
+ errorCode: raw.errorCode
434
+ } : {},
435
+ ...raw.fromDay !== void 0 ? {
436
+ fromDay: raw.fromDay
437
+ } : {},
438
+ ...raw.toDay !== void 0 ? {
439
+ toDay: raw.toDay
440
+ } : {}
441
+ };
442
+ }
443
+ __name(parseRunWhere, "parseRunWhere");
444
+
303
445
  // src/server/agent-api.controller.ts
304
446
  function _ts_decorate2(decorators, target, key, desc) {
305
447
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -319,13 +461,13 @@ function _ts_param2(paramIndex, decorator) {
319
461
  }
320
462
  __name(_ts_param2, "_ts_param");
321
463
  var DAY_MS = 864e5;
322
- var ISO_DAY = /^\d{4}-\d{2}-\d{2}$/;
464
+ var ISO_DAY2 = /^\d{4}-\d{2}-\d{2}$/;
323
465
  function utcDay(daysAgo) {
324
466
  return new Date(Date.now() - daysAgo * DAY_MS).toISOString().slice(0, 10);
325
467
  }
326
468
  __name(utcDay, "utcDay");
327
469
  function dayOr(value, fallback) {
328
- return value !== void 0 && ISO_DAY.test(value) ? value : fallback;
470
+ return value !== void 0 && ISO_DAY2.test(value) ? value : fallback;
329
471
  }
330
472
  __name(dayOr, "dayOr");
331
473
  function resolveRange(from, to) {
@@ -347,9 +489,11 @@ var AgentApiController = class {
347
489
  }
348
490
  dashboard;
349
491
  approvalActorRef;
350
- constructor(dashboard, approvalActorRef) {
492
+ actorResolver;
493
+ constructor(dashboard, approvalActorRef, actorResolver) {
351
494
  this.dashboard = dashboard;
352
495
  this.approvalActorRef = approvalActorRef;
496
+ this.actorResolver = actorResolver;
353
497
  }
354
498
  /** `{ byModel, byActor, trend }` for a day range (defaults to the last 30 days). */
355
499
  spend(from, to) {
@@ -367,10 +511,40 @@ var AgentApiController = class {
367
511
  runs(limit) {
368
512
  return this.dashboard.recentRuns(parseLimit(limit, 50));
369
513
  }
514
+ /**
515
+ * Paged, filterable runs for the Reliability recent-runs table. `page` (default 1), `limit`
516
+ * (default 25, max 200), and `where[agentName]`/`where[status]`/`where[errorCode]`/
517
+ * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
518
+ */
519
+ runsPage(page, limit, where) {
520
+ const parsedWhere = parseRunWhere(where);
521
+ return this.dashboard.runsPage({
522
+ page: parsePageNumber(page),
523
+ pageSize: parseLimit(limit, 25),
524
+ ...parsedWhere !== void 0 ? {
525
+ where: parsedWhere
526
+ } : {}
527
+ });
528
+ }
370
529
  /** Most recent tool calls (default 50, max 200) for the activity feed. */
371
530
  toolCalls(limit) {
372
531
  return this.dashboard.recentToolCalls(parseLimit(limit, 50));
373
532
  }
533
+ /**
534
+ * Paged, filterable tool calls for the Runs & tools activity table. `page` (default 1), `limit`
535
+ * (default 25, max 200), and `where[toolName]`/`where[toolType]`/`where[status]`/`where[threadId]`/
536
+ * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
537
+ */
538
+ toolCallsPage(page, limit, where) {
539
+ const parsedWhere = parseToolCallWhere(where);
540
+ return this.dashboard.toolCallsPage({
541
+ page: parsePageNumber(page),
542
+ pageSize: parseLimit(limit, 25),
543
+ ...parsedWhere !== void 0 ? {
544
+ where: parsedWhere
545
+ } : {}
546
+ });
547
+ }
374
548
  /** Tool calls sitting `pending_approval` (default 50, max 200), oldest first — the approvals inbox. */
375
549
  approvals(limit) {
376
550
  return this.dashboard.pendingApprovals(parseLimit(limit, 50));
@@ -378,10 +552,30 @@ var AgentApiController = class {
378
552
  /**
379
553
  * Decide a pending HITL tool call. Body `{ approved: boolean; reason?: string }`. 501s (via
380
554
  * `DashboardService.decideApproval`) when no `AGENT_APPROVAL_PORT` is bound. `executedByRef`
381
- * comes from the host's `approvalActorRef` extractor run against the live request, when configured.
555
+ * comes from {@link deciderRef} run against the live request.
382
556
  */
383
557
  async decideApproval(toolCallId, body, req) {
384
- await this.dashboard.decideApproval(toolCallId, body, this.approvalActorRef?.(req));
558
+ await this.dashboard.decideApproval(toolCallId, body, await this.deciderRef(req));
559
+ }
560
+ /**
561
+ * WHO decided, as an opaque ref: an explicit `approvalActorRef` override wins outright (no
562
+ * resolver fallback, even when it returns `undefined`); otherwise the AgentModule-configured
563
+ * actor resolver — the same identity seam chat requests use. A resolver that throws is an
564
+ * unauthenticated/unreadable request, not an error to surface: the decision itself was already
565
+ * authorized by the dashboard's guards, so the ref is simply omitted.
566
+ */
567
+ async deciderRef(req) {
568
+ if (this.approvalActorRef !== void 0) {
569
+ return this.approvalActorRef(req);
570
+ }
571
+ if (this.actorResolver === void 0) {
572
+ return void 0;
573
+ }
574
+ try {
575
+ return (await this.actorResolver.resolve(req)).id;
576
+ } catch {
577
+ return void 0;
578
+ }
385
579
  }
386
580
  /** Per-tool call/failure/rejection/latency rollup for a day range (defaults to the last 30 days). */
387
581
  tools(from, to) {
@@ -392,6 +586,21 @@ var AgentApiController = class {
392
586
  return this.dashboard.recentThreads(parseLimit(limit, 50));
393
587
  }
394
588
  /**
589
+ * Paged, filterable threads for the Runs & tools threads table. `page` (default 1), `limit`
590
+ * (default 25, max 200), and `where[actorRef]`/`where[title]` (case-insensitive substring)/
591
+ * `where[fromDay]`/`where[toDay]` (`YYYY-MM-DD`) — an unknown `where` field 400s naming it.
592
+ */
593
+ threadsPage(page, limit, where) {
594
+ const parsedWhere = parseThreadWhere(where);
595
+ return this.dashboard.threadsPage({
596
+ page: parsePageNumber(page),
597
+ pageSize: parseLimit(limit, 25),
598
+ ...parsedWhere !== void 0 ? {
599
+ where: parsedWhere
600
+ } : {}
601
+ });
602
+ }
603
+ /**
395
604
  * Current price row per model, for the pricing tab. 501s (via `DashboardService.listPrices`) when
396
605
  * no `AGENT_PRICING_STORE` is bound.
397
606
  */
@@ -455,6 +664,19 @@ _ts_decorate2([
455
664
  ]),
456
665
  _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
457
666
  ], AgentApiController.prototype, "runs", null);
667
+ _ts_decorate2([
668
+ Get("runs-page"),
669
+ _ts_param2(0, Query("page")),
670
+ _ts_param2(1, Query("limit")),
671
+ _ts_param2(2, Query("where")),
672
+ _ts_metadata2("design:type", Function),
673
+ _ts_metadata2("design:paramtypes", [
674
+ String,
675
+ String,
676
+ typeof Record === "undefined" ? Object : Record
677
+ ]),
678
+ _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
679
+ ], AgentApiController.prototype, "runsPage", null);
458
680
  _ts_decorate2([
459
681
  Get("tool-calls"),
460
682
  _ts_param2(0, Query("limit")),
@@ -464,6 +686,19 @@ _ts_decorate2([
464
686
  ]),
465
687
  _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
466
688
  ], AgentApiController.prototype, "toolCalls", null);
689
+ _ts_decorate2([
690
+ Get("tool-calls-page"),
691
+ _ts_param2(0, Query("page")),
692
+ _ts_param2(1, Query("limit")),
693
+ _ts_param2(2, Query("where")),
694
+ _ts_metadata2("design:type", Function),
695
+ _ts_metadata2("design:paramtypes", [
696
+ String,
697
+ String,
698
+ typeof Record === "undefined" ? Object : Record
699
+ ]),
700
+ _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
701
+ ], AgentApiController.prototype, "toolCallsPage", null);
467
702
  _ts_decorate2([
468
703
  Get("approvals"),
469
704
  _ts_param2(0, Query("limit")),
@@ -507,6 +742,19 @@ _ts_decorate2([
507
742
  ]),
508
743
  _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
509
744
  ], AgentApiController.prototype, "threads", null);
745
+ _ts_decorate2([
746
+ Get("threads-page"),
747
+ _ts_param2(0, Query("page")),
748
+ _ts_param2(1, Query("limit")),
749
+ _ts_param2(2, Query("where")),
750
+ _ts_metadata2("design:type", Function),
751
+ _ts_metadata2("design:paramtypes", [
752
+ String,
753
+ String,
754
+ typeof Record === "undefined" ? Object : Record
755
+ ]),
756
+ _ts_metadata2("design:returntype", typeof Promise === "undefined" ? Object : Promise)
757
+ ], AgentApiController.prototype, "threadsPage", null);
510
758
  _ts_decorate2([
511
759
  Get("pricing"),
512
760
  _ts_metadata2("design:type", Function),
@@ -531,10 +779,13 @@ _ts_decorate2([
531
779
  AgentApiController = _ts_decorate2([
532
780
  Controller(),
533
781
  _ts_param2(1, Inject2(DASHBOARD_APPROVAL_ACTOR_REF)),
782
+ _ts_param2(2, Optional2()),
783
+ _ts_param2(2, Inject2(AGENT_ACTOR_RESOLVER)),
534
784
  _ts_metadata2("design:type", Function),
535
785
  _ts_metadata2("design:paramtypes", [
536
786
  typeof DashboardService === "undefined" ? Object : DashboardService,
537
- Object
787
+ Object,
788
+ typeof ActorResolver === "undefined" ? Object : ActorResolver
538
789
  ])
539
790
  ], AgentApiController);
540
791
 
@@ -572,7 +823,7 @@ import { RouterModule } from "@nestjs/core";
572
823
  import { existsSync, readFileSync } from "node:fs";
573
824
  import { basename, extname, join, resolve, sep } from "node:path";
574
825
  import { fileURLToPath } from "node:url";
575
- import { Controller as Controller2, Get as Get2, Header, Inject as Inject3, NotFoundException, Param as Param2, StreamableFile } from "@nestjs/common";
826
+ import { Controller as Controller2, Get as Get2, Header, Inject as Inject3, NotFoundException, Param as Param2, Req as Req2, Res, StreamableFile } from "@nestjs/common";
576
827
  function _ts_decorate3(decorators, target, key, desc) {
577
828
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
578
829
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -617,7 +868,12 @@ var AgentUiController = class {
617
868
  }
618
869
  // index.html references hash-named bundles, so it MUST NOT be cached (stale bundle = the classic
619
870
  // "stuck loading after a deploy"). The hashed assets below are immutable.
620
- index() {
871
+ index(req, res) {
872
+ if (req.headers["x-inertia"] !== void 0) {
873
+ res.status(409);
874
+ res.setHeader("X-Inertia-Location", req.originalUrl);
875
+ return "";
876
+ }
621
877
  const indexPath = join(this.dir, "index.html");
622
878
  if (!existsSync(indexPath)) {
623
879
  throw new NotFoundException("Dashboard is not built. Run the package build.");
@@ -644,8 +900,15 @@ _ts_decorate3([
644
900
  Get2(),
645
901
  Header("Content-Type", "text/html; charset=utf-8"),
646
902
  Header("Cache-Control", "no-store, must-revalidate"),
903
+ _ts_param3(0, Req2()),
904
+ _ts_param3(1, Res({
905
+ passthrough: true
906
+ })),
647
907
  _ts_metadata3("design:type", Function),
648
- _ts_metadata3("design:paramtypes", []),
908
+ _ts_metadata3("design:paramtypes", [
909
+ typeof UiPageRequest === "undefined" ? Object : UiPageRequest,
910
+ typeof UiPageResponse === "undefined" ? Object : UiPageResponse
911
+ ]),
649
912
  _ts_metadata3("design:returntype", String)
650
913
  ], AgentUiController.prototype, "index", null);
651
914
  _ts_decorate3([
@@ -784,6 +1047,7 @@ AgentDashboardModule = _ts_decorate4([
784
1047
  ], AgentDashboardModule);
785
1048
  export {
786
1049
  AGENT_ACTOR_DIRECTORY,
1050
+ AGENT_ACTOR_RESOLVER,
787
1051
  AGENT_APPROVAL_PORT,
788
1052
  AGENT_GOVERNANCE_QUERIES,
789
1053
  AGENT_PRICING_STORE,