@dudousxd/nestjs-agent-telescope 0.3.0 → 0.3.2

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/index.cjs CHANGED
@@ -192,6 +192,40 @@ function agentDashboard() {
192
192
  }
193
193
  ]
194
194
  },
195
+ {
196
+ title: "Threads",
197
+ panels: [
198
+ {
199
+ kind: "table",
200
+ title: "Top threads by cost",
201
+ data: {
202
+ provider: "agent.threads.topSpend"
203
+ },
204
+ columns: [
205
+ {
206
+ key: "title",
207
+ label: "Thread"
208
+ },
209
+ {
210
+ key: "actorRef",
211
+ label: "Actor"
212
+ },
213
+ {
214
+ key: "requests",
215
+ label: "Requests"
216
+ },
217
+ {
218
+ key: "totalTokens",
219
+ label: "Tokens"
220
+ },
221
+ {
222
+ key: "costUsd",
223
+ label: "Cost (USD)"
224
+ }
225
+ ]
226
+ }
227
+ ]
228
+ },
195
229
  {
196
230
  title: "Tools",
197
231
  cols: 2,
@@ -322,12 +356,13 @@ __name(agentToolStatusProvider, "agentToolStatusProvider");
322
356
  var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
323
357
  var DEFAULT_TREND_WINDOW_DAYS = 30;
324
358
  var ISO_DAY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
359
+ var TOP_THREADS_LIMIT = 10;
325
360
  function isRecord(value) {
326
361
  return typeof value === "object" && value !== null;
327
362
  }
328
363
  __name(isRecord, "isRecord");
329
364
  function isGovernanceQueries(value) {
330
- return isRecord(value) && typeof value.spendByModel === "function" && typeof value.spendByActor === "function" && typeof value.usageTrend === "function" && typeof value.recentToolCalls === "function" && typeof value.recentThreads === "function";
365
+ return isRecord(value) && typeof value.spendByModel === "function" && typeof value.spendByActor === "function" && typeof value.spendByThread === "function" && typeof value.usageTrend === "function" && typeof value.recentToolCalls === "function" && typeof value.recentThreads === "function";
331
366
  }
332
367
  __name(isGovernanceQueries, "isGovernanceQueries");
333
368
  function resolveGovernanceQueries(ctx) {
@@ -403,6 +438,16 @@ function toActorSpendRows(rows) {
403
438
  }));
404
439
  }
405
440
  __name(toActorSpendRows, "toActorSpendRows");
441
+ function toThreadSpendRows(rows) {
442
+ return rows.map((row) => ({
443
+ title: row.title || row.threadId,
444
+ actorRef: row.actorRef,
445
+ requests: row.requests,
446
+ totalTokens: row.totalTokens,
447
+ costUsd: roundCents(row.costUsd)
448
+ }));
449
+ }
450
+ __name(toThreadSpendRows, "toThreadSpendRows");
406
451
  function toActorSpendSegments(rows) {
407
452
  return rows.filter((row) => row.costUsd > 0).map((row) => ({
408
453
  label: row.actorRef,
@@ -418,132 +463,65 @@ function toUsageTrendRows(points) {
418
463
  }));
419
464
  }
420
465
  __name(toUsageTrendRows, "toUsageTrendRows");
421
- function agentSpendTotalProvider() {
466
+ function governanceStatProvider(name, fetch, format) {
422
467
  return {
423
- name: "agent.spend.totalCost",
468
+ name,
424
469
  async resolve(query, ctx) {
425
470
  const queries = resolveGovernanceQueries(ctx);
426
- if (!queries) {
427
- return {
428
- value: 0
429
- };
430
- }
431
- const byModel = await queries.spendByModel(resolveRange(query));
432
- return {
433
- value: totalCostUsd(byModel)
434
- };
471
+ const rows = queries ? await fetch(queries, resolveRange(query)) : [];
472
+ return format(rows);
435
473
  }
436
474
  };
437
475
  }
476
+ __name(governanceStatProvider, "governanceStatProvider");
477
+ function agentSpendTotalProvider() {
478
+ return governanceStatProvider("agent.spend.totalCost", (queries, range) => queries.spendByModel(range), (rows) => ({
479
+ value: totalCostUsd(rows)
480
+ }));
481
+ }
438
482
  __name(agentSpendTotalProvider, "agentSpendTotalProvider");
439
483
  function agentTokensTotalProvider() {
440
- return {
441
- name: "agent.spend.totalTokens",
442
- async resolve(query, ctx) {
443
- const queries = resolveGovernanceQueries(ctx);
444
- if (!queries) {
445
- return {
446
- value: 0
447
- };
448
- }
449
- const byModel = await queries.spendByModel(resolveRange(query));
450
- return {
451
- value: totalTokens(byModel)
452
- };
453
- }
454
- };
484
+ return governanceStatProvider("agent.spend.totalTokens", (queries, range) => queries.spendByModel(range), (rows) => ({
485
+ value: totalTokens(rows)
486
+ }));
455
487
  }
456
488
  __name(agentTokensTotalProvider, "agentTokensTotalProvider");
457
489
  function agentSpendByModelProvider() {
458
- return {
459
- name: "agent.spend.byModel",
460
- async resolve(query, ctx) {
461
- const queries = resolveGovernanceQueries(ctx);
462
- if (!queries) {
463
- return {
464
- segments: []
465
- };
466
- }
467
- const byModel = await queries.spendByModel(resolveRange(query));
468
- return {
469
- segments: toModelSpendSegments(byModel)
470
- };
471
- }
472
- };
490
+ return governanceStatProvider("agent.spend.byModel", (queries, range) => queries.spendByModel(range), (rows) => ({
491
+ segments: toModelSpendSegments(rows)
492
+ }));
473
493
  }
474
494
  __name(agentSpendByModelProvider, "agentSpendByModelProvider");
475
495
  function agentModelSpendTableProvider() {
476
- return {
477
- name: "agent.spend.byModelTable",
478
- async resolve(query, ctx) {
479
- const queries = resolveGovernanceQueries(ctx);
480
- if (!queries) {
481
- return {
482
- rows: []
483
- };
484
- }
485
- const byModel = await queries.spendByModel(resolveRange(query));
486
- return {
487
- rows: toModelSpendRows(byModel)
488
- };
489
- }
490
- };
496
+ return governanceStatProvider("agent.spend.byModelTable", (queries, range) => queries.spendByModel(range), (rows) => ({
497
+ rows: toModelSpendRows(rows)
498
+ }));
491
499
  }
492
500
  __name(agentModelSpendTableProvider, "agentModelSpendTableProvider");
493
501
  function agentUsageTrendProvider() {
494
- return {
495
- name: "agent.usage.trend",
496
- async resolve(query, ctx) {
497
- const queries = resolveGovernanceQueries(ctx);
498
- if (!queries) {
499
- return {
500
- rows: []
501
- };
502
- }
503
- const trend = await queries.usageTrend(resolveRange(query));
504
- return {
505
- rows: toUsageTrendRows(trend)
506
- };
507
- }
508
- };
502
+ return governanceStatProvider("agent.usage.trend", (queries, range) => queries.usageTrend(range), (points) => ({
503
+ rows: toUsageTrendRows(points)
504
+ }));
509
505
  }
510
506
  __name(agentUsageTrendProvider, "agentUsageTrendProvider");
511
507
  function agentActorSpendTableProvider() {
512
- return {
513
- name: "agent.spend.byActor",
514
- async resolve(query, ctx) {
515
- const queries = resolveGovernanceQueries(ctx);
516
- if (!queries) {
517
- return {
518
- rows: []
519
- };
520
- }
521
- const byActor = await queries.spendByActor(resolveRange(query));
522
- return {
523
- rows: toActorSpendRows(byActor)
524
- };
525
- }
526
- };
508
+ return governanceStatProvider("agent.spend.byActor", (queries, range) => queries.spendByActor(range), (rows) => ({
509
+ rows: toActorSpendRows(rows)
510
+ }));
527
511
  }
528
512
  __name(agentActorSpendTableProvider, "agentActorSpendTableProvider");
529
513
  function agentSpendByActorProvider() {
530
- return {
531
- name: "agent.spend.byActorShare",
532
- async resolve(query, ctx) {
533
- const queries = resolveGovernanceQueries(ctx);
534
- if (!queries) {
535
- return {
536
- segments: []
537
- };
538
- }
539
- const byActor = await queries.spendByActor(resolveRange(query));
540
- return {
541
- segments: toActorSpendSegments(byActor)
542
- };
543
- }
544
- };
514
+ return governanceStatProvider("agent.spend.byActorShare", (queries, range) => queries.spendByActor(range), (rows) => ({
515
+ segments: toActorSpendSegments(rows)
516
+ }));
545
517
  }
546
518
  __name(agentSpendByActorProvider, "agentSpendByActorProvider");
519
+ function agentTopThreadsTableProvider() {
520
+ return governanceStatProvider("agent.threads.topSpend", (queries, range) => queries.spendByThread(range, TOP_THREADS_LIMIT), (rows) => ({
521
+ rows: toThreadSpendRows(rows)
522
+ }));
523
+ }
524
+ __name(agentTopThreadsTableProvider, "agentTopThreadsTableProvider");
547
525
 
548
526
  // src/agent-telescope.watcher.ts
549
527
  var import_node_diagnostics_channel = require("diagnostics_channel");
@@ -607,7 +585,8 @@ function agentTelescopeExtension() {
607
585
  agentModelSpendTableProvider(),
608
586
  agentUsageTrendProvider(),
609
587
  agentActorSpendTableProvider(),
610
- agentSpendByActorProvider()
588
+ agentSpendByActorProvider(),
589
+ agentTopThreadsTableProvider()
611
590
  ], "dataProviders")
612
591
  });
613
592
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/agent-telescope.extension.ts","../src/agent-dashboard.ts","../src/agent-data-providers.ts","../src/agent-governance-providers.ts","../src/agent-telescope.watcher.ts"],"sourcesContent":["export { agentTelescopeExtension } from './agent-telescope.extension.js';\nexport { AgentTelescopeWatcher } from './agent-telescope.watcher.js';\nexport { agentDashboard } from './agent-dashboard.js';\nexport {\n agentRunsProvider,\n agentTokensProvider,\n agentToolsProvider,\n agentToolStatusProvider,\n} from './agent-data-providers.js';\nexport {\n agentActorSpendTableProvider,\n agentModelSpendTableProvider,\n agentSpendByActorProvider,\n agentSpendByModelProvider,\n agentSpendTotalProvider,\n agentTokensTotalProvider,\n agentUsageTrendProvider,\n resolveRange,\n shiftUtcDay,\n toActorSpendRows,\n toActorSpendSegments,\n toModelSpendRows,\n toModelSpendSegments,\n totalCostUsd,\n totalTokens,\n toUsageTrendRows,\n} from './agent-governance-providers.js';\n","import { defineTelescopeExtension } from '@dudousxd/nestjs-telescope';\nimport { agentDashboard } from './agent-dashboard.js';\nimport {\n agentRunsProvider,\n agentTokensProvider,\n agentToolStatusProvider,\n agentToolsProvider,\n} from './agent-data-providers.js';\nimport {\n agentActorSpendTableProvider,\n agentModelSpendTableProvider,\n agentSpendByActorProvider,\n agentSpendByModelProvider,\n agentSpendTotalProvider,\n agentTokensTotalProvider,\n agentUsageTrendProvider,\n} from './agent-governance-providers.js';\nimport { AgentTelescopeWatcher } from './agent-telescope.watcher.js';\n\n/**\n * The first-class Telescope extension for nestjs-agent: an \"Agent\" tab fed by two sources —\n * the `aviary:agent:*` diagnostics channel (live runs, tool calls) via the watcher, and the\n * authoritative `AGENT_GOVERNANCE_QUERIES` read-model (historical spend/usage) via the governance\n * providers. The extension `name`, entry-type id, dashboard id, and every provider name share the\n * `agent` prefix so the registry's global-uniqueness namespaces never collide with sibling extensions.\n *\n * Host wiring: the governance (Spend/Models/Actors) panels resolve `AGENT_GOVERNANCE_QUERIES` from\n * the host DI container at request time (via `ctx.moduleRef`). The host must bind that token — from\n * its store adapter (e.g. `store-mikro-orm` / `store-drizzle` / `testing`) — in the same module that\n * registers `TelescopeModule.forRoot({ extensions: [agentTelescopeExtension()] })`. If the binding is\n * absent, those panels render an empty state; the live watcher-fed panels keep working regardless.\n */\nexport function agentTelescopeExtension() {\n return defineTelescopeExtension({\n name: 'agent',\n watchers: () => [new AgentTelescopeWatcher()],\n entryTypes: () => [{ id: 'agent', label: 'Agent', dot: 'bg-violet-400' }],\n dashboards: () => [agentDashboard()],\n dataProviders: () => [\n agentRunsProvider(),\n agentTokensProvider(),\n agentToolsProvider(),\n agentToolStatusProvider(),\n agentSpendTotalProvider(),\n agentTokensTotalProvider(),\n agentSpendByModelProvider(),\n agentModelSpendTableProvider(),\n agentUsageTrendProvider(),\n agentActorSpendTableProvider(),\n agentSpendByActorProvider(),\n ],\n });\n}\n","import type { DashboardSpec } from '@dudousxd/nestjs-telescope';\n\n/** The \"Agent\" overview dashboard. Panels bind to the `agent.*` data providers. */\nexport function agentDashboard(): DashboardSpec {\n return {\n id: 'agent.overview',\n label: 'Agent',\n panels: [],\n sections: [\n {\n title: 'Overview',\n cols: 2,\n panels: [\n { kind: 'stat', title: 'Runs', data: { provider: 'agent.runs' } },\n { kind: 'stat', title: 'Tokens', data: { provider: 'agent.tokens' } },\n ],\n },\n {\n title: 'Spend',\n cols: 2,\n panels: [\n {\n kind: 'stat',\n title: 'Total spend (USD)',\n data: { provider: 'agent.spend.totalCost' },\n format: 'number',\n },\n {\n kind: 'stat',\n title: 'Total tokens',\n data: { provider: 'agent.spend.totalTokens' },\n format: 'number',\n },\n {\n kind: 'breakdown',\n title: 'Spend by model',\n data: { provider: 'agent.spend.byModel' },\n style: 'donut',\n },\n {\n kind: 'timeseries',\n title: 'Daily spend & tokens',\n data: { provider: 'agent.usage.trend' },\n series: ['costUsd', 'totalTokens'],\n style: 'area',\n },\n ],\n },\n {\n title: 'Models',\n panels: [\n {\n kind: 'table',\n title: 'Usage & cost by model',\n data: { provider: 'agent.spend.byModelTable' },\n columns: [\n { key: 'modelId', label: 'Model' },\n { key: 'requests', label: 'Requests' },\n { key: 'inputTokens', label: 'Input tokens' },\n { key: 'outputTokens', label: 'Output tokens' },\n { key: 'costUsd', label: 'Cost (USD)' },\n ],\n },\n ],\n },\n {\n title: 'Actors',\n cols: 2,\n panels: [\n {\n kind: 'breakdown',\n title: 'Spend share by actor',\n data: { provider: 'agent.spend.byActorShare' },\n style: 'bar',\n },\n {\n kind: 'table',\n title: 'Spend by actor',\n data: { provider: 'agent.spend.byActor' },\n columns: [\n { key: 'actorRef', label: 'Actor' },\n { key: 'requests', label: 'Requests' },\n { key: 'totalTokens', label: 'Tokens' },\n { key: 'costUsd', label: 'Cost (USD)' },\n ],\n },\n ],\n },\n {\n title: 'Tools',\n cols: 2,\n panels: [\n {\n kind: 'breakdown',\n title: 'Tool-call status',\n data: { provider: 'agent.toolStatus' },\n style: 'donut',\n },\n {\n kind: 'table',\n title: 'Recent tool calls',\n data: { provider: 'agent.tools' },\n columns: [\n { key: 'toolName', label: 'Tool' },\n { key: 'toolType', label: 'Type' },\n { key: 'status', label: 'Status' },\n { key: 'runId', label: 'Run' },\n ],\n },\n ],\n },\n ],\n };\n}\n","import type { DataProvider, ExtensionContext } from '@dudousxd/nestjs-telescope';\nimport { TELESCOPE_STORAGE } from '@dudousxd/nestjs-telescope';\n\ninterface AgentEntryContent {\n event: string;\n runId?: string;\n threadId?: string;\n toolName?: string;\n toolType?: string;\n status?: string;\n steps?: number;\n inputTokens?: number;\n outputTokens?: number;\n}\n\ninterface StorageEntry {\n content?: AgentEntryContent;\n createdAt?: Date;\n}\n\nasync function fetchEntries(ctx: ExtensionContext): Promise<StorageEntry[]> {\n const storage = ctx.moduleRef.get(TELESCOPE_STORAGE, { strict: false }) as {\n get(query: { type?: string; limit?: number }): Promise<{ data: StorageEntry[] }>;\n };\n const page = await storage.get({ type: 'agent', limit: 5_000 });\n return page.data;\n}\n\nfunction ofEvent(entries: StorageEntry[], event: string): StorageEntry[] {\n return entries.filter((entry) => entry.content?.event === event);\n}\n\n/** stat → total agent runs (run.finished entries). */\nexport function agentRunsProvider(): DataProvider {\n return {\n name: 'agent.runs',\n async resolve(_query, ctx) {\n const finished = ofEvent(await fetchEntries(ctx), 'run.finished');\n return { value: finished.length };\n },\n };\n}\n\n/** stat → total tokens across all finished runs. */\nexport function agentTokensProvider(): DataProvider {\n return {\n name: 'agent.tokens',\n async resolve(_query, ctx) {\n const finished = ofEvent(await fetchEntries(ctx), 'run.finished');\n const value = finished.reduce(\n (sum, entry) =>\n sum + (entry.content?.inputTokens ?? 0) + (entry.content?.outputTokens ?? 0),\n 0,\n );\n return { value };\n },\n };\n}\n\n/** table → recent tool calls. */\nexport function agentToolsProvider(): DataProvider {\n return {\n name: 'agent.tools',\n async resolve(_query, ctx) {\n const calls = ofEvent(await fetchEntries(ctx), 'tool-call')\n .slice(-50)\n .reverse()\n .map((entry) => ({\n toolName: entry.content?.toolName ?? '',\n toolType: entry.content?.toolType ?? '',\n status: entry.content?.status ?? '',\n runId: entry.content?.runId ?? '',\n }));\n return { rows: calls };\n },\n };\n}\n\n/** breakdown → tool-call status distribution. */\nexport function agentToolStatusProvider(): DataProvider {\n return {\n name: 'agent.toolStatus',\n async resolve(_query, ctx) {\n const calls = ofEvent(await fetchEntries(ctx), 'tool-call');\n const counts = new Map<string, number>();\n for (const call of calls) {\n const status = call.content?.status ?? 'unknown';\n counts.set(status, (counts.get(status) ?? 0) + 1);\n }\n return { segments: [...counts.entries()].map(([label, value]) => ({ label, value })) };\n },\n };\n}\n","import type {\n ActorSpendRow,\n AgentGovernanceQueries,\n GovernanceRange,\n ModelSpendRow,\n UsageTrendPoint,\n} from '@dudousxd/nestjs-agent-core';\nimport { AGENT_GOVERNANCE_QUERIES } from '@dudousxd/nestjs-agent-core';\nimport type { DataProvider, ExtensionContext } from '@dudousxd/nestjs-telescope';\n\n/**\n * Governance data providers for the \"Agent\" Telescope tab. Unlike the live watcher-fed providers\n * in `agent-data-providers.ts` (which read the ephemeral Telescope event storage), these read the\n * authoritative, restart-surviving read-model: `AGENT_GOVERNANCE_QUERIES` (usage ⋈ pricing).\n *\n * The read-model is resolved from the host's DI container via `ctx.moduleRef` — the exact same\n * mechanism the existing providers use for `TELESCOPE_STORAGE`. The host must bind\n * `AGENT_GOVERNANCE_QUERIES` (from its store adapter) in the same module that registers Telescope;\n * when the binding is absent every provider degrades to an empty-but-valid shape rather than throwing.\n */\n\n/** Default trailing window (in days, inclusive) when a panel query omits an explicit range. */\nconst DEFAULT_TREND_WINDOW_DAYS = 30;\n\nconst ISO_DAY_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** A breakdown-panel segment (Telescope core: `{ segments: Array<{ label, value, color? }> }`). */\ninterface BreakdownSegment {\n label: string;\n value: number;\n}\n\n/** One row of the per-model usage/cost table. */\ninterface ModelSpendTableRow {\n modelId: string;\n requests: number;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n}\n\n/** One row of the per-actor spend table. */\ninterface ActorSpendTableRow {\n actorRef: string;\n requests: number;\n totalTokens: number;\n costUsd: number;\n}\n\n/** One point of the timeseries trend (Telescope core: `{ label } & Record<string, number>`). */\ninterface UsageTrendTableRow {\n label: string;\n costUsd: number;\n totalTokens: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Structural guard so the DI-resolved binding is narrowed without an `as`/`any` cast. */\nfunction isGovernanceQueries(value: unknown): value is AgentGovernanceQueries {\n return (\n isRecord(value) &&\n typeof value.spendByModel === 'function' &&\n typeof value.spendByActor === 'function' &&\n typeof value.usageTrend === 'function' &&\n typeof value.recentToolCalls === 'function' &&\n typeof value.recentThreads === 'function'\n );\n}\n\n/**\n * Resolve the governance read-model from the host container. Returns `null` when the host has not\n * bound `AGENT_GOVERNANCE_QUERIES` (missing token throws inside `moduleRef.get`) so panels can render\n * an empty state instead of erroring.\n */\nfunction resolveGovernanceQueries(ctx: ExtensionContext): AgentGovernanceQueries | null {\n let resolved: unknown;\n try {\n resolved = ctx.moduleRef.get(AGENT_GOVERNANCE_QUERIES, { strict: false });\n } catch {\n return null;\n }\n return isGovernanceQueries(resolved) ? resolved : null;\n}\n\nfunction isIsoDay(value: unknown): value is string {\n return typeof value === 'string' && ISO_DAY_PATTERN.test(value);\n}\n\nfunction todayUtcDay(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\n/** Shift a `YYYY-MM-DD` UTC day by `deltaDays` (negative goes back in time). */\nexport function shiftUtcDay(day: string, deltaDays: number): string {\n const shifted = new Date(`${day}T00:00:00.000Z`);\n shifted.setUTCDate(shifted.getUTCDate() + deltaDays);\n return shifted.toISOString().slice(0, 10);\n}\n\n/**\n * Derive the query range: honour explicit `fromDay`/`toDay` (validated ISO days), otherwise default\n * to the trailing {@link DEFAULT_TREND_WINDOW_DAYS}-day window ending today (UTC).\n */\nexport function resolveRange(query: Record<string, unknown> | undefined): GovernanceRange {\n const toDay = query && isIsoDay(query.toDay) ? query.toDay : todayUtcDay();\n const fromDay =\n query && isIsoDay(query.fromDay)\n ? query.fromDay\n : shiftUtcDay(toDay, -(DEFAULT_TREND_WINDOW_DAYS - 1));\n return { fromDay, toDay };\n}\n\nfunction roundCents(value: number): number {\n return Math.round(value * 100) / 100;\n}\n\n/** Sum authoritative spend across every model row. */\nexport function totalCostUsd(rows: ModelSpendRow[]): number {\n return roundCents(rows.reduce((sum, row) => sum + row.costUsd, 0));\n}\n\n/** Sum input + output tokens across every model row. */\nexport function totalTokens(rows: ModelSpendRow[]): number {\n return rows.reduce((sum, row) => sum + row.inputTokens + row.outputTokens, 0);\n}\n\n/** Spend-by-model as breakdown segments (models with zero cost are dropped from the donut). */\nexport function toModelSpendSegments(rows: ModelSpendRow[]): BreakdownSegment[] {\n return rows\n .filter((row) => row.costUsd > 0)\n .map((row) => ({ label: row.modelId, value: roundCents(row.costUsd) }));\n}\n\n/** Spend-by-model as table rows (cost rounded to cents; usage kept exact). */\nexport function toModelSpendRows(rows: ModelSpendRow[]): ModelSpendTableRow[] {\n return rows.map((row) => ({\n modelId: row.modelId,\n requests: row.requests,\n inputTokens: row.inputTokens,\n outputTokens: row.outputTokens,\n costUsd: roundCents(row.costUsd),\n }));\n}\n\n/** Spend-by-actor as table rows. */\nexport function toActorSpendRows(rows: ActorSpendRow[]): ActorSpendTableRow[] {\n return rows.map((row) => ({\n actorRef: row.actorRef,\n requests: row.requests,\n totalTokens: row.totalTokens,\n costUsd: roundCents(row.costUsd),\n }));\n}\n\n/** Spend-by-actor as breakdown segments (actors with zero cost are dropped). */\nexport function toActorSpendSegments(rows: ActorSpendRow[]): BreakdownSegment[] {\n return rows\n .filter((row) => row.costUsd > 0)\n .map((row) => ({ label: row.actorRef, value: roundCents(row.costUsd) }));\n}\n\n/** Daily usage trend as timeseries rows keyed by `label` (the day). */\nexport function toUsageTrendRows(points: UsageTrendPoint[]): UsageTrendTableRow[] {\n return points.map((point) => ({\n label: point.day,\n costUsd: roundCents(point.costUsd),\n totalTokens: point.totalTokens,\n }));\n}\n\n/** stat → authoritative total spend (USD) over the range. */\nexport function agentSpendTotalProvider(): DataProvider {\n return {\n name: 'agent.spend.totalCost',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { value: 0 };\n }\n const byModel = await queries.spendByModel(resolveRange(query));\n return { value: totalCostUsd(byModel) };\n },\n };\n}\n\n/** stat → authoritative total tokens (input + output) over the range. */\nexport function agentTokensTotalProvider(): DataProvider {\n return {\n name: 'agent.spend.totalTokens',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { value: 0 };\n }\n const byModel = await queries.spendByModel(resolveRange(query));\n return { value: totalTokens(byModel) };\n },\n };\n}\n\n/** breakdown → spend share per model. */\nexport function agentSpendByModelProvider(): DataProvider {\n return {\n name: 'agent.spend.byModel',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { segments: [] };\n }\n const byModel = await queries.spendByModel(resolveRange(query));\n return { segments: toModelSpendSegments(byModel) };\n },\n };\n}\n\n/** table → per-model requests / in+out tokens / cost. */\nexport function agentModelSpendTableProvider(): DataProvider {\n return {\n name: 'agent.spend.byModelTable',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { rows: [] };\n }\n const byModel = await queries.spendByModel(resolveRange(query));\n return { rows: toModelSpendRows(byModel) };\n },\n };\n}\n\n/** timeseries → daily spend + tokens trend. */\nexport function agentUsageTrendProvider(): DataProvider {\n return {\n name: 'agent.usage.trend',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { rows: [] };\n }\n const trend = await queries.usageTrend(resolveRange(query));\n return { rows: toUsageTrendRows(trend) };\n },\n };\n}\n\n/** table → spend per acting ref (user/tenant). */\nexport function agentActorSpendTableProvider(): DataProvider {\n return {\n name: 'agent.spend.byActor',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { rows: [] };\n }\n const byActor = await queries.spendByActor(resolveRange(query));\n return { rows: toActorSpendRows(byActor) };\n },\n };\n}\n\n/** breakdown → spend share per actor. */\nexport function agentSpendByActorProvider(): DataProvider {\n return {\n name: 'agent.spend.byActorShare',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { segments: [] };\n }\n const byActor = await queries.spendByActor(resolveRange(query));\n return { segments: toActorSpendSegments(byActor) };\n },\n };\n}\n","import { subscribe } from 'node:diagnostics_channel';\nimport { channelName } from '@dudousxd/nestjs-diagnostics';\nimport type { Watcher, WatcherContext } from '@dudousxd/nestjs-telescope';\n\nconst AGENT_EVENTS = ['run.started', 'message', 'tool-call', 'quota.exceeded', 'run.finished'];\n\ninterface DiagnosticEnvelope {\n event: string;\n payload: Record<string, unknown>;\n}\n\n/**\n * Records `aviary:agent:*` diagnostics events as Telescope entries of type `agent`. It depends\n * only on the diagnostics channel — not on the agent runtime — so it stays fully decoupled.\n */\nexport class AgentTelescopeWatcher implements Watcher {\n readonly type = 'agent';\n\n register(ctx: WatcherContext): void {\n for (const event of AGENT_EVENTS) {\n subscribe(channelName('agent', event), (message) => {\n const envelope = message as DiagnosticEnvelope;\n ctx.record({\n type: 'agent',\n content: { event: envelope.event, ...envelope.payload },\n tags: [envelope.event],\n });\n });\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAA,IAAAA,2BAAyC;;;ACGlC,SAASC,iBAAAA;AACd,SAAO;IACLC,IAAI;IACJC,OAAO;IACPC,QAAQ,CAAA;IACRC,UAAU;MACR;QACEC,OAAO;QACPC,MAAM;QACNH,QAAQ;UACN;YAAEI,MAAM;YAAQF,OAAO;YAAQG,MAAM;cAAEC,UAAU;YAAa;UAAE;UAChE;YAAEF,MAAM;YAAQF,OAAO;YAAUG,MAAM;cAAEC,UAAU;YAAe;UAAE;;MAExE;MACA;QACEJ,OAAO;QACPC,MAAM;QACNH,QAAQ;UACN;YACEI,MAAM;YACNF,OAAO;YACPG,MAAM;cAAEC,UAAU;YAAwB;YAC1CC,QAAQ;UACV;UACA;YACEH,MAAM;YACNF,OAAO;YACPG,MAAM;cAAEC,UAAU;YAA0B;YAC5CC,QAAQ;UACV;UACA;YACEH,MAAM;YACNF,OAAO;YACPG,MAAM;cAAEC,UAAU;YAAsB;YACxCE,OAAO;UACT;UACA;YACEJ,MAAM;YACNF,OAAO;YACPG,MAAM;cAAEC,UAAU;YAAoB;YACtCG,QAAQ;cAAC;cAAW;;YACpBD,OAAO;UACT;;MAEJ;MACA;QACEN,OAAO;QACPF,QAAQ;UACN;YACEI,MAAM;YACNF,OAAO;YACPG,MAAM;cAAEC,UAAU;YAA2B;YAC7CI,SAAS;cACP;gBAAEC,KAAK;gBAAWZ,OAAO;cAAQ;cACjC;gBAAEY,KAAK;gBAAYZ,OAAO;cAAW;cACrC;gBAAEY,KAAK;gBAAeZ,OAAO;cAAe;cAC5C;gBAAEY,KAAK;gBAAgBZ,OAAO;cAAgB;cAC9C;gBAAEY,KAAK;gBAAWZ,OAAO;cAAa;;UAE1C;;MAEJ;MACA;QACEG,OAAO;QACPC,MAAM;QACNH,QAAQ;UACN;YACEI,MAAM;YACNF,OAAO;YACPG,MAAM;cAAEC,UAAU;YAA2B;YAC7CE,OAAO;UACT;UACA;YACEJ,MAAM;YACNF,OAAO;YACPG,MAAM;cAAEC,UAAU;YAAsB;YACxCI,SAAS;cACP;gBAAEC,KAAK;gBAAYZ,OAAO;cAAQ;cAClC;gBAAEY,KAAK;gBAAYZ,OAAO;cAAW;cACrC;gBAAEY,KAAK;gBAAeZ,OAAO;cAAS;cACtC;gBAAEY,KAAK;gBAAWZ,OAAO;cAAa;;UAE1C;;MAEJ;MACA;QACEG,OAAO;QACPC,MAAM;QACNH,QAAQ;UACN;YACEI,MAAM;YACNF,OAAO;YACPG,MAAM;cAAEC,UAAU;YAAmB;YACrCE,OAAO;UACT;UACA;YACEJ,MAAM;YACNF,OAAO;YACPG,MAAM;cAAEC,UAAU;YAAc;YAChCI,SAAS;cACP;gBAAEC,KAAK;gBAAYZ,OAAO;cAAO;cACjC;gBAAEY,KAAK;gBAAYZ,OAAO;cAAO;cACjC;gBAAEY,KAAK;gBAAUZ,OAAO;cAAS;cACjC;gBAAEY,KAAK;gBAASZ,OAAO;cAAM;;UAEjC;;MAEJ;;EAEJ;AACF;AA9GgBF;;;ACFhB,8BAAkC;AAmBlC,eAAee,aAAaC,KAAqB;AAC/C,QAAMC,UAAUD,IAAIE,UAAUC,IAAIC,2CAAmB;IAAEC,QAAQ;EAAM,CAAA;AAGrE,QAAMC,OAAO,MAAML,QAAQE,IAAI;IAAEI,MAAM;IAASC,OAAO;EAAM,CAAA;AAC7D,SAAOF,KAAKG;AACd;AANeV;AAQf,SAASW,QAAQC,SAAyBC,OAAa;AACrD,SAAOD,QAAQE,OAAO,CAACC,UAAUA,MAAMC,SAASH,UAAUA,KAAAA;AAC5D;AAFSF;AAKF,SAASM,oBAAAA;AACd,SAAO;IACLC,MAAM;IACN,MAAMC,QAAQC,QAAQnB,KAAG;AACvB,YAAMoB,WAAWV,QAAQ,MAAMX,aAAaC,GAAAA,GAAM,cAAA;AAClD,aAAO;QAAEqB,OAAOD,SAASE;MAAO;IAClC;EACF;AACF;AARgBN;AAWT,SAASO,sBAAAA;AACd,SAAO;IACLN,MAAM;IACN,MAAMC,QAAQC,QAAQnB,KAAG;AACvB,YAAMoB,WAAWV,QAAQ,MAAMX,aAAaC,GAAAA,GAAM,cAAA;AAClD,YAAMqB,QAAQD,SAASI,OACrB,CAACC,KAAKX,UACJW,OAAOX,MAAMC,SAASW,eAAe,MAAMZ,MAAMC,SAASY,gBAAgB,IAC5E,CAAA;AAEF,aAAO;QAAEN;MAAM;IACjB;EACF;AACF;AAbgBE;AAgBT,SAASK,qBAAAA;AACd,SAAO;IACLX,MAAM;IACN,MAAMC,QAAQC,QAAQnB,KAAG;AACvB,YAAM6B,QAAQnB,QAAQ,MAAMX,aAAaC,GAAAA,GAAM,WAAA,EAC5C8B,MAAM,GAAC,EACPC,QAAO,EACPC,IAAI,CAAClB,WAAW;QACfmB,UAAUnB,MAAMC,SAASkB,YAAY;QACrCC,UAAUpB,MAAMC,SAASmB,YAAY;QACrCC,QAAQrB,MAAMC,SAASoB,UAAU;QACjCC,OAAOtB,MAAMC,SAASqB,SAAS;MACjC,EAAA;AACF,aAAO;QAAEC,MAAMR;MAAM;IACvB;EACF;AACF;AAhBgBD;AAmBT,SAASU,0BAAAA;AACd,SAAO;IACLrB,MAAM;IACN,MAAMC,QAAQC,QAAQnB,KAAG;AACvB,YAAM6B,QAAQnB,QAAQ,MAAMX,aAAaC,GAAAA,GAAM,WAAA;AAC/C,YAAMuC,SAAS,oBAAIC,IAAAA;AACnB,iBAAWC,QAAQZ,OAAO;AACxB,cAAMM,SAASM,KAAK1B,SAASoB,UAAU;AACvCI,eAAOG,IAAIP,SAASI,OAAOpC,IAAIgC,MAAAA,KAAW,KAAK,CAAA;MACjD;AACA,aAAO;QAAEQ,UAAU;aAAIJ,OAAO5B,QAAO;UAAIqB,IAAI,CAAC,CAACY,OAAOvB,KAAAA,OAAY;UAAEuB;UAAOvB;QAAM,EAAA;MAAI;IACvF;EACF;AACF;AAbgBiB;;;ACxEhB,+BAAyC;AAezC,IAAMO,4BAA4B;AAElC,IAAMC,kBAAkB;AAgCxB,SAASC,SAASC,OAAc;AAC9B,SAAO,OAAOA,UAAU,YAAYA,UAAU;AAChD;AAFSD;AAKT,SAASE,oBAAoBD,OAAc;AACzC,SACED,SAASC,KAAAA,KACT,OAAOA,MAAME,iBAAiB,cAC9B,OAAOF,MAAMG,iBAAiB,cAC9B,OAAOH,MAAMI,eAAe,cAC5B,OAAOJ,MAAMK,oBAAoB,cACjC,OAAOL,MAAMM,kBAAkB;AAEnC;AATSL;AAgBT,SAASM,yBAAyBC,KAAqB;AACrD,MAAIC;AACJ,MAAI;AACFA,eAAWD,IAAIE,UAAUC,IAAIC,mDAA0B;MAAEC,QAAQ;IAAM,CAAA;EACzE,QAAQ;AACN,WAAO;EACT;AACA,SAAOZ,oBAAoBQ,QAAAA,IAAYA,WAAW;AACpD;AARSF;AAUT,SAASO,SAASd,OAAc;AAC9B,SAAO,OAAOA,UAAU,YAAYF,gBAAgBiB,KAAKf,KAAAA;AAC3D;AAFSc;AAIT,SAASE,cAAAA;AACP,UAAO,oBAAIC,KAAAA,GAAOC,YAAW,EAAGC,MAAM,GAAG,EAAA;AAC3C;AAFSH;AAKF,SAASI,YAAYC,KAAaC,WAAiB;AACxD,QAAMC,UAAU,oBAAIN,KAAK,GAAGI,GAAAA,gBAAmB;AAC/CE,UAAQC,WAAWD,QAAQE,WAAU,IAAKH,SAAAA;AAC1C,SAAOC,QAAQL,YAAW,EAAGC,MAAM,GAAG,EAAA;AACxC;AAJgBC;AAUT,SAASM,aAAaC,OAA0C;AACrE,QAAMC,QAAQD,SAASb,SAASa,MAAMC,KAAK,IAAID,MAAMC,QAAQZ,YAAAA;AAC7D,QAAMa,UACJF,SAASb,SAASa,MAAME,OAAO,IAC3BF,MAAME,UACNT,YAAYQ,OAAO,EAAE/B,4BAA4B,EAAA;AACvD,SAAO;IAAEgC;IAASD;EAAM;AAC1B;AAPgBF;AAShB,SAASI,WAAW9B,OAAa;AAC/B,SAAO+B,KAAKC,MAAMhC,QAAQ,GAAA,IAAO;AACnC;AAFS8B;AAKF,SAASG,aAAaC,MAAqB;AAChD,SAAOJ,WAAWI,KAAKC,OAAO,CAACC,KAAKC,QAAQD,MAAMC,IAAIC,SAAS,CAAA,CAAA;AACjE;AAFgBL;AAKT,SAASM,YAAYL,MAAqB;AAC/C,SAAOA,KAAKC,OAAO,CAACC,KAAKC,QAAQD,MAAMC,IAAIG,cAAcH,IAAII,cAAc,CAAA;AAC7E;AAFgBF;AAKT,SAASG,qBAAqBR,MAAqB;AACxD,SAAOA,KACJS,OAAO,CAACN,QAAQA,IAAIC,UAAU,CAAA,EAC9BM,IAAI,CAACP,SAAS;IAAEQ,OAAOR,IAAIS;IAAS9C,OAAO8B,WAAWO,IAAIC,OAAO;EAAE,EAAA;AACxE;AAJgBI;AAOT,SAASK,iBAAiBb,MAAqB;AACpD,SAAOA,KAAKU,IAAI,CAACP,SAAS;IACxBS,SAAST,IAAIS;IACbE,UAAUX,IAAIW;IACdR,aAAaH,IAAIG;IACjBC,cAAcJ,IAAII;IAClBH,SAASR,WAAWO,IAAIC,OAAO;EACjC,EAAA;AACF;AARgBS;AAWT,SAASE,iBAAiBf,MAAqB;AACpD,SAAOA,KAAKU,IAAI,CAACP,SAAS;IACxBa,UAAUb,IAAIa;IACdF,UAAUX,IAAIW;IACdT,aAAaF,IAAIE;IACjBD,SAASR,WAAWO,IAAIC,OAAO;EACjC,EAAA;AACF;AAPgBW;AAUT,SAASE,qBAAqBjB,MAAqB;AACxD,SAAOA,KACJS,OAAO,CAACN,QAAQA,IAAIC,UAAU,CAAA,EAC9BM,IAAI,CAACP,SAAS;IAAEQ,OAAOR,IAAIa;IAAUlD,OAAO8B,WAAWO,IAAIC,OAAO;EAAE,EAAA;AACzE;AAJgBa;AAOT,SAASC,iBAAiBC,QAAyB;AACxD,SAAOA,OAAOT,IAAI,CAACU,WAAW;IAC5BT,OAAOS,MAAMjC;IACbiB,SAASR,WAAWwB,MAAMhB,OAAO;IACjCC,aAAae,MAAMf;EACrB,EAAA;AACF;AANgBa;AAST,SAASG,0BAAAA;AACd,SAAO;IACLC,MAAM;IACN,MAAMC,QAAQ9B,OAAOnB,KAAG;AACtB,YAAMkD,UAAUnD,yBAAyBC,GAAAA;AACzC,UAAI,CAACkD,SAAS;AACZ,eAAO;UAAE1D,OAAO;QAAE;MACpB;AACA,YAAM2D,UAAU,MAAMD,QAAQxD,aAAawB,aAAaC,KAAAA,CAAAA;AACxD,aAAO;QAAE3B,OAAOiC,aAAa0B,OAAAA;MAAS;IACxC;EACF;AACF;AAZgBJ;AAeT,SAASK,2BAAAA;AACd,SAAO;IACLJ,MAAM;IACN,MAAMC,QAAQ9B,OAAOnB,KAAG;AACtB,YAAMkD,UAAUnD,yBAAyBC,GAAAA;AACzC,UAAI,CAACkD,SAAS;AACZ,eAAO;UAAE1D,OAAO;QAAE;MACpB;AACA,YAAM2D,UAAU,MAAMD,QAAQxD,aAAawB,aAAaC,KAAAA,CAAAA;AACxD,aAAO;QAAE3B,OAAOuC,YAAYoB,OAAAA;MAAS;IACvC;EACF;AACF;AAZgBC;AAeT,SAASC,4BAAAA;AACd,SAAO;IACLL,MAAM;IACN,MAAMC,QAAQ9B,OAAOnB,KAAG;AACtB,YAAMkD,UAAUnD,yBAAyBC,GAAAA;AACzC,UAAI,CAACkD,SAAS;AACZ,eAAO;UAAEI,UAAU,CAAA;QAAG;MACxB;AACA,YAAMH,UAAU,MAAMD,QAAQxD,aAAawB,aAAaC,KAAAA,CAAAA;AACxD,aAAO;QAAEmC,UAAUpB,qBAAqBiB,OAAAA;MAAS;IACnD;EACF;AACF;AAZgBE;AAeT,SAASE,+BAAAA;AACd,SAAO;IACLP,MAAM;IACN,MAAMC,QAAQ9B,OAAOnB,KAAG;AACtB,YAAMkD,UAAUnD,yBAAyBC,GAAAA;AACzC,UAAI,CAACkD,SAAS;AACZ,eAAO;UAAExB,MAAM,CAAA;QAAG;MACpB;AACA,YAAMyB,UAAU,MAAMD,QAAQxD,aAAawB,aAAaC,KAAAA,CAAAA;AACxD,aAAO;QAAEO,MAAMa,iBAAiBY,OAAAA;MAAS;IAC3C;EACF;AACF;AAZgBI;AAeT,SAASC,0BAAAA;AACd,SAAO;IACLR,MAAM;IACN,MAAMC,QAAQ9B,OAAOnB,KAAG;AACtB,YAAMkD,UAAUnD,yBAAyBC,GAAAA;AACzC,UAAI,CAACkD,SAAS;AACZ,eAAO;UAAExB,MAAM,CAAA;QAAG;MACpB;AACA,YAAM+B,QAAQ,MAAMP,QAAQtD,WAAWsB,aAAaC,KAAAA,CAAAA;AACpD,aAAO;QAAEO,MAAMkB,iBAAiBa,KAAAA;MAAO;IACzC;EACF;AACF;AAZgBD;AAeT,SAASE,+BAAAA;AACd,SAAO;IACLV,MAAM;IACN,MAAMC,QAAQ9B,OAAOnB,KAAG;AACtB,YAAMkD,UAAUnD,yBAAyBC,GAAAA;AACzC,UAAI,CAACkD,SAAS;AACZ,eAAO;UAAExB,MAAM,CAAA;QAAG;MACpB;AACA,YAAMiC,UAAU,MAAMT,QAAQvD,aAAauB,aAAaC,KAAAA,CAAAA;AACxD,aAAO;QAAEO,MAAMe,iBAAiBkB,OAAAA;MAAS;IAC3C;EACF;AACF;AAZgBD;AAeT,SAASE,4BAAAA;AACd,SAAO;IACLZ,MAAM;IACN,MAAMC,QAAQ9B,OAAOnB,KAAG;AACtB,YAAMkD,UAAUnD,yBAAyBC,GAAAA;AACzC,UAAI,CAACkD,SAAS;AACZ,eAAO;UAAEI,UAAU,CAAA;QAAG;MACxB;AACA,YAAMK,UAAU,MAAMT,QAAQvD,aAAauB,aAAaC,KAAAA,CAAAA;AACxD,aAAO;QAAEmC,UAAUX,qBAAqBgB,OAAAA;MAAS;IACnD;EACF;AACF;AAZgBC;;;ACxQhB,sCAA0B;AAC1B,gCAA4B;AAG5B,IAAMC,eAAe;EAAC;EAAe;EAAW;EAAa;EAAkB;;AAWxE,IAAMC,wBAAN,MAAMA;EAfb,OAeaA;;;EACFC,OAAO;EAEhBC,SAASC,KAA2B;AAClC,eAAWC,SAASL,cAAc;AAChCM,yDAAUC,uCAAY,SAASF,KAAAA,GAAQ,CAACG,YAAAA;AACtC,cAAMC,WAAWD;AACjBJ,YAAIM,OAAO;UACTR,MAAM;UACNS,SAAS;YAAEN,OAAOI,SAASJ;YAAO,GAAGI,SAASG;UAAQ;UACtDC,MAAM;YAACJ,SAASJ;;QAClB,CAAA;MACF,CAAA;IACF;EACF;AACF;;;AJEO,SAASS,0BAAAA;AACd,aAAOC,mDAAyB;IAC9BC,MAAM;IACNC,UAAU,6BAAM;MAAC,IAAIC,sBAAAA;OAAX;IACVC,YAAY,6BAAM;MAAC;QAAEC,IAAI;QAASC,OAAO;QAASC,KAAK;MAAgB;OAA3D;IACZC,YAAY,6BAAM;MAACC,eAAAA;OAAP;IACZC,eAAe,6BAAM;MACnBC,kBAAAA;MACAC,oBAAAA;MACAC,mBAAAA;MACAC,wBAAAA;MACAC,wBAAAA;MACAC,yBAAAA;MACAC,0BAAAA;MACAC,6BAAAA;MACAC,wBAAAA;MACAC,6BAAAA;MACAC,0BAAAA;OAXa;EAajB,CAAA;AACF;AApBgBtB;","names":["import_nestjs_telescope","agentDashboard","id","label","panels","sections","title","cols","kind","data","provider","format","style","series","columns","key","fetchEntries","ctx","storage","moduleRef","get","TELESCOPE_STORAGE","strict","page","type","limit","data","ofEvent","entries","event","filter","entry","content","agentRunsProvider","name","resolve","_query","finished","value","length","agentTokensProvider","reduce","sum","inputTokens","outputTokens","agentToolsProvider","calls","slice","reverse","map","toolName","toolType","status","runId","rows","agentToolStatusProvider","counts","Map","call","set","segments","label","DEFAULT_TREND_WINDOW_DAYS","ISO_DAY_PATTERN","isRecord","value","isGovernanceQueries","spendByModel","spendByActor","usageTrend","recentToolCalls","recentThreads","resolveGovernanceQueries","ctx","resolved","moduleRef","get","AGENT_GOVERNANCE_QUERIES","strict","isIsoDay","test","todayUtcDay","Date","toISOString","slice","shiftUtcDay","day","deltaDays","shifted","setUTCDate","getUTCDate","resolveRange","query","toDay","fromDay","roundCents","Math","round","totalCostUsd","rows","reduce","sum","row","costUsd","totalTokens","inputTokens","outputTokens","toModelSpendSegments","filter","map","label","modelId","toModelSpendRows","requests","toActorSpendRows","actorRef","toActorSpendSegments","toUsageTrendRows","points","point","agentSpendTotalProvider","name","resolve","queries","byModel","agentTokensTotalProvider","agentSpendByModelProvider","segments","agentModelSpendTableProvider","agentUsageTrendProvider","trend","agentActorSpendTableProvider","byActor","agentSpendByActorProvider","AGENT_EVENTS","AgentTelescopeWatcher","type","register","ctx","event","subscribe","channelName","message","envelope","record","content","payload","tags","agentTelescopeExtension","defineTelescopeExtension","name","watchers","AgentTelescopeWatcher","entryTypes","id","label","dot","dashboards","agentDashboard","dataProviders","agentRunsProvider","agentTokensProvider","agentToolsProvider","agentToolStatusProvider","agentSpendTotalProvider","agentTokensTotalProvider","agentSpendByModelProvider","agentModelSpendTableProvider","agentUsageTrendProvider","agentActorSpendTableProvider","agentSpendByActorProvider"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/agent-telescope.extension.ts","../src/agent-dashboard.ts","../src/agent-data-providers.ts","../src/agent-governance-providers.ts","../src/agent-telescope.watcher.ts"],"sourcesContent":["export { agentTelescopeExtension } from './agent-telescope.extension.js';\nexport { AgentTelescopeWatcher } from './agent-telescope.watcher.js';\nexport { agentDashboard } from './agent-dashboard.js';\nexport {\n agentRunsProvider,\n agentTokensProvider,\n agentToolsProvider,\n agentToolStatusProvider,\n} from './agent-data-providers.js';\nexport {\n agentActorSpendTableProvider,\n agentModelSpendTableProvider,\n agentSpendByActorProvider,\n agentSpendByModelProvider,\n agentSpendTotalProvider,\n agentTokensTotalProvider,\n agentUsageTrendProvider,\n resolveRange,\n shiftUtcDay,\n toActorSpendRows,\n toActorSpendSegments,\n toModelSpendRows,\n toModelSpendSegments,\n totalCostUsd,\n totalTokens,\n toUsageTrendRows,\n} from './agent-governance-providers.js';\n","import { defineTelescopeExtension } from '@dudousxd/nestjs-telescope';\nimport { agentDashboard } from './agent-dashboard.js';\nimport {\n agentRunsProvider,\n agentTokensProvider,\n agentToolStatusProvider,\n agentToolsProvider,\n} from './agent-data-providers.js';\nimport {\n agentActorSpendTableProvider,\n agentModelSpendTableProvider,\n agentSpendByActorProvider,\n agentSpendByModelProvider,\n agentSpendTotalProvider,\n agentTokensTotalProvider,\n agentTopThreadsTableProvider,\n agentUsageTrendProvider,\n} from './agent-governance-providers.js';\nimport { AgentTelescopeWatcher } from './agent-telescope.watcher.js';\n\n/**\n * The first-class Telescope extension for nestjs-agent: an \"Agent\" tab fed by two sources —\n * the `aviary:agent:*` diagnostics channel (live runs, tool calls) via the watcher, and the\n * authoritative `AGENT_GOVERNANCE_QUERIES` read-model (historical spend/usage) via the governance\n * providers. The extension `name`, entry-type id, dashboard id, and every provider name share the\n * `agent` prefix so the registry's global-uniqueness namespaces never collide with sibling extensions.\n *\n * Host wiring: the governance (Spend/Models/Actors) panels resolve `AGENT_GOVERNANCE_QUERIES` from\n * the host DI container at request time (via `ctx.moduleRef`). The host must bind that token — from\n * its store adapter (e.g. `store-mikro-orm` / `store-drizzle` / `testing`) — in the same module that\n * registers `TelescopeModule.forRoot({ extensions: [agentTelescopeExtension()] })`. If the binding is\n * absent, those panels render an empty state; the live watcher-fed panels keep working regardless.\n */\nexport function agentTelescopeExtension() {\n return defineTelescopeExtension({\n name: 'agent',\n watchers: () => [new AgentTelescopeWatcher()],\n entryTypes: () => [{ id: 'agent', label: 'Agent', dot: 'bg-violet-400' }],\n dashboards: () => [agentDashboard()],\n dataProviders: () => [\n agentRunsProvider(),\n agentTokensProvider(),\n agentToolsProvider(),\n agentToolStatusProvider(),\n agentSpendTotalProvider(),\n agentTokensTotalProvider(),\n agentSpendByModelProvider(),\n agentModelSpendTableProvider(),\n agentUsageTrendProvider(),\n agentActorSpendTableProvider(),\n agentSpendByActorProvider(),\n agentTopThreadsTableProvider(),\n ],\n });\n}\n","import type { DashboardSpec } from '@dudousxd/nestjs-telescope';\n\n/** The \"Agent\" overview dashboard. Panels bind to the `agent.*` data providers. */\nexport function agentDashboard(): DashboardSpec {\n return {\n id: 'agent.overview',\n label: 'Agent',\n panels: [],\n sections: [\n {\n title: 'Overview',\n cols: 2,\n panels: [\n { kind: 'stat', title: 'Runs', data: { provider: 'agent.runs' } },\n { kind: 'stat', title: 'Tokens', data: { provider: 'agent.tokens' } },\n ],\n },\n {\n title: 'Spend',\n cols: 2,\n panels: [\n {\n kind: 'stat',\n title: 'Total spend (USD)',\n data: { provider: 'agent.spend.totalCost' },\n format: 'number',\n },\n {\n kind: 'stat',\n title: 'Total tokens',\n data: { provider: 'agent.spend.totalTokens' },\n format: 'number',\n },\n {\n kind: 'breakdown',\n title: 'Spend by model',\n data: { provider: 'agent.spend.byModel' },\n style: 'donut',\n },\n {\n kind: 'timeseries',\n title: 'Daily spend & tokens',\n data: { provider: 'agent.usage.trend' },\n series: ['costUsd', 'totalTokens'],\n style: 'area',\n },\n ],\n },\n {\n title: 'Models',\n panels: [\n {\n kind: 'table',\n title: 'Usage & cost by model',\n data: { provider: 'agent.spend.byModelTable' },\n columns: [\n { key: 'modelId', label: 'Model' },\n { key: 'requests', label: 'Requests' },\n { key: 'inputTokens', label: 'Input tokens' },\n { key: 'outputTokens', label: 'Output tokens' },\n { key: 'costUsd', label: 'Cost (USD)' },\n ],\n },\n ],\n },\n {\n title: 'Actors',\n cols: 2,\n panels: [\n {\n kind: 'breakdown',\n title: 'Spend share by actor',\n data: { provider: 'agent.spend.byActorShare' },\n style: 'bar',\n },\n {\n kind: 'table',\n title: 'Spend by actor',\n data: { provider: 'agent.spend.byActor' },\n columns: [\n { key: 'actorRef', label: 'Actor' },\n { key: 'requests', label: 'Requests' },\n { key: 'totalTokens', label: 'Tokens' },\n { key: 'costUsd', label: 'Cost (USD)' },\n ],\n },\n ],\n },\n {\n title: 'Threads',\n panels: [\n {\n kind: 'table',\n title: 'Top threads by cost',\n data: { provider: 'agent.threads.topSpend' },\n columns: [\n { key: 'title', label: 'Thread' },\n { key: 'actorRef', label: 'Actor' },\n { key: 'requests', label: 'Requests' },\n { key: 'totalTokens', label: 'Tokens' },\n { key: 'costUsd', label: 'Cost (USD)' },\n ],\n },\n ],\n },\n {\n title: 'Tools',\n cols: 2,\n panels: [\n {\n kind: 'breakdown',\n title: 'Tool-call status',\n data: { provider: 'agent.toolStatus' },\n style: 'donut',\n },\n {\n kind: 'table',\n title: 'Recent tool calls',\n data: { provider: 'agent.tools' },\n columns: [\n { key: 'toolName', label: 'Tool' },\n { key: 'toolType', label: 'Type' },\n { key: 'status', label: 'Status' },\n { key: 'runId', label: 'Run' },\n ],\n },\n ],\n },\n ],\n };\n}\n","import type { DataProvider, ExtensionContext } from '@dudousxd/nestjs-telescope';\nimport { TELESCOPE_STORAGE } from '@dudousxd/nestjs-telescope';\n\ninterface AgentEntryContent {\n event: string;\n runId?: string;\n threadId?: string;\n toolName?: string;\n toolType?: string;\n status?: string;\n steps?: number;\n inputTokens?: number;\n outputTokens?: number;\n}\n\ninterface StorageEntry {\n content?: AgentEntryContent;\n createdAt?: Date;\n}\n\nasync function fetchEntries(ctx: ExtensionContext): Promise<StorageEntry[]> {\n const storage = ctx.moduleRef.get(TELESCOPE_STORAGE, { strict: false }) as {\n get(query: { type?: string; limit?: number }): Promise<{ data: StorageEntry[] }>;\n };\n const page = await storage.get({ type: 'agent', limit: 5_000 });\n return page.data;\n}\n\nfunction ofEvent(entries: StorageEntry[], event: string): StorageEntry[] {\n return entries.filter((entry) => entry.content?.event === event);\n}\n\n/** stat → total agent runs (run.finished entries). */\nexport function agentRunsProvider(): DataProvider {\n return {\n name: 'agent.runs',\n async resolve(_query, ctx) {\n const finished = ofEvent(await fetchEntries(ctx), 'run.finished');\n return { value: finished.length };\n },\n };\n}\n\n/** stat → total tokens across all finished runs. */\nexport function agentTokensProvider(): DataProvider {\n return {\n name: 'agent.tokens',\n async resolve(_query, ctx) {\n const finished = ofEvent(await fetchEntries(ctx), 'run.finished');\n const value = finished.reduce(\n (sum, entry) =>\n sum + (entry.content?.inputTokens ?? 0) + (entry.content?.outputTokens ?? 0),\n 0,\n );\n return { value };\n },\n };\n}\n\n/** table → recent tool calls. */\nexport function agentToolsProvider(): DataProvider {\n return {\n name: 'agent.tools',\n async resolve(_query, ctx) {\n const calls = ofEvent(await fetchEntries(ctx), 'tool-call')\n .slice(-50)\n .reverse()\n .map((entry) => ({\n toolName: entry.content?.toolName ?? '',\n toolType: entry.content?.toolType ?? '',\n status: entry.content?.status ?? '',\n runId: entry.content?.runId ?? '',\n }));\n return { rows: calls };\n },\n };\n}\n\n/** breakdown → tool-call status distribution. */\nexport function agentToolStatusProvider(): DataProvider {\n return {\n name: 'agent.toolStatus',\n async resolve(_query, ctx) {\n const calls = ofEvent(await fetchEntries(ctx), 'tool-call');\n const counts = new Map<string, number>();\n for (const call of calls) {\n const status = call.content?.status ?? 'unknown';\n counts.set(status, (counts.get(status) ?? 0) + 1);\n }\n return { segments: [...counts.entries()].map(([label, value]) => ({ label, value })) };\n },\n };\n}\n","import type {\n ActorSpendRow,\n AgentGovernanceQueries,\n GovernanceRange,\n ModelSpendRow,\n ThreadSpendRow,\n UsageTrendPoint,\n} from '@dudousxd/nestjs-agent-core';\nimport { AGENT_GOVERNANCE_QUERIES } from '@dudousxd/nestjs-agent-core';\nimport type { DataProvider, ExtensionContext } from '@dudousxd/nestjs-telescope';\n\n/**\n * Governance data providers for the \"Agent\" Telescope tab. Unlike the live watcher-fed providers\n * in `agent-data-providers.ts` (which read the ephemeral Telescope event storage), these read the\n * authoritative, restart-surviving read-model: `AGENT_GOVERNANCE_QUERIES` (usage ⋈ pricing).\n *\n * The read-model is resolved from the host's DI container via `ctx.moduleRef` — the exact same\n * mechanism the existing providers use for `TELESCOPE_STORAGE`. The host must bind\n * `AGENT_GOVERNANCE_QUERIES` (from its store adapter) in the same module that registers Telescope;\n * when the binding is absent every provider degrades to an empty-but-valid shape rather than throwing.\n */\n\n/** Default trailing window (in days, inclusive) when a panel query omits an explicit range. */\nconst DEFAULT_TREND_WINDOW_DAYS = 30;\n\nconst ISO_DAY_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** A breakdown-panel segment (Telescope core: `{ segments: Array<{ label, value, color? }> }`). */\ninterface BreakdownSegment {\n label: string;\n value: number;\n}\n\n/** One row of the per-model usage/cost table. */\ninterface ModelSpendTableRow {\n modelId: string;\n requests: number;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n}\n\n/** One row of the per-actor spend table. */\ninterface ActorSpendTableRow {\n actorRef: string;\n requests: number;\n totalTokens: number;\n costUsd: number;\n}\n\n/** One row of the top-threads-by-cost table. */\ninterface ThreadSpendTableRow {\n title: string;\n actorRef: string;\n requests: number;\n totalTokens: number;\n costUsd: number;\n}\n\n/** Top threads by cost within the range, capped to this count. */\nconst TOP_THREADS_LIMIT = 10;\n\n/** One point of the timeseries trend (Telescope core: `{ label } & Record<string, number>`). */\ninterface UsageTrendTableRow {\n label: string;\n costUsd: number;\n totalTokens: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Structural guard so the DI-resolved binding is narrowed without an `as`/`any` cast. */\nfunction isGovernanceQueries(value: unknown): value is AgentGovernanceQueries {\n return (\n isRecord(value) &&\n typeof value.spendByModel === 'function' &&\n typeof value.spendByActor === 'function' &&\n typeof value.spendByThread === 'function' &&\n typeof value.usageTrend === 'function' &&\n typeof value.recentToolCalls === 'function' &&\n typeof value.recentThreads === 'function'\n );\n}\n\n/**\n * Resolve the governance read-model from the host container. Returns `null` when the host has not\n * bound `AGENT_GOVERNANCE_QUERIES` (missing token throws inside `moduleRef.get`) so panels can render\n * an empty state instead of erroring.\n */\nfunction resolveGovernanceQueries(ctx: ExtensionContext): AgentGovernanceQueries | null {\n let resolved: unknown;\n try {\n resolved = ctx.moduleRef.get(AGENT_GOVERNANCE_QUERIES, { strict: false });\n } catch {\n return null;\n }\n return isGovernanceQueries(resolved) ? resolved : null;\n}\n\nfunction isIsoDay(value: unknown): value is string {\n return typeof value === 'string' && ISO_DAY_PATTERN.test(value);\n}\n\nfunction todayUtcDay(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\n/** Shift a `YYYY-MM-DD` UTC day by `deltaDays` (negative goes back in time). */\nexport function shiftUtcDay(day: string, deltaDays: number): string {\n const shifted = new Date(`${day}T00:00:00.000Z`);\n shifted.setUTCDate(shifted.getUTCDate() + deltaDays);\n return shifted.toISOString().slice(0, 10);\n}\n\n/**\n * Derive the query range: honour explicit `fromDay`/`toDay` (validated ISO days), otherwise default\n * to the trailing {@link DEFAULT_TREND_WINDOW_DAYS}-day window ending today (UTC).\n */\nexport function resolveRange(query: Record<string, unknown> | undefined): GovernanceRange {\n const toDay = query && isIsoDay(query.toDay) ? query.toDay : todayUtcDay();\n const fromDay =\n query && isIsoDay(query.fromDay)\n ? query.fromDay\n : shiftUtcDay(toDay, -(DEFAULT_TREND_WINDOW_DAYS - 1));\n return { fromDay, toDay };\n}\n\nfunction roundCents(value: number): number {\n return Math.round(value * 100) / 100;\n}\n\n/** Sum authoritative spend across every model row. */\nexport function totalCostUsd(rows: ModelSpendRow[]): number {\n return roundCents(rows.reduce((sum, row) => sum + row.costUsd, 0));\n}\n\n/** Sum input + output tokens across every model row. */\nexport function totalTokens(rows: ModelSpendRow[]): number {\n return rows.reduce((sum, row) => sum + row.inputTokens + row.outputTokens, 0);\n}\n\n/** Spend-by-model as breakdown segments (models with zero cost are dropped from the donut). */\nexport function toModelSpendSegments(rows: ModelSpendRow[]): BreakdownSegment[] {\n return rows\n .filter((row) => row.costUsd > 0)\n .map((row) => ({ label: row.modelId, value: roundCents(row.costUsd) }));\n}\n\n/** Spend-by-model as table rows (cost rounded to cents; usage kept exact). */\nexport function toModelSpendRows(rows: ModelSpendRow[]): ModelSpendTableRow[] {\n return rows.map((row) => ({\n modelId: row.modelId,\n requests: row.requests,\n inputTokens: row.inputTokens,\n outputTokens: row.outputTokens,\n costUsd: roundCents(row.costUsd),\n }));\n}\n\n/** Spend-by-actor as table rows. */\nexport function toActorSpendRows(rows: ActorSpendRow[]): ActorSpendTableRow[] {\n return rows.map((row) => ({\n actorRef: row.actorRef,\n requests: row.requests,\n totalTokens: row.totalTokens,\n costUsd: roundCents(row.costUsd),\n }));\n}\n\n/** Top-threads-by-cost as table rows. */\nexport function toThreadSpendRows(rows: ThreadSpendRow[]): ThreadSpendTableRow[] {\n return rows.map((row) => ({\n title: row.title || row.threadId,\n actorRef: row.actorRef,\n requests: row.requests,\n totalTokens: row.totalTokens,\n costUsd: roundCents(row.costUsd),\n }));\n}\n\n/** Spend-by-actor as breakdown segments (actors with zero cost are dropped). */\nexport function toActorSpendSegments(rows: ActorSpendRow[]): BreakdownSegment[] {\n return rows\n .filter((row) => row.costUsd > 0)\n .map((row) => ({ label: row.actorRef, value: roundCents(row.costUsd) }));\n}\n\n/** Daily usage trend as timeseries rows keyed by `label` (the day). */\nexport function toUsageTrendRows(points: UsageTrendPoint[]): UsageTrendTableRow[] {\n return points.map((point) => ({\n label: point.day,\n costUsd: roundCents(point.costUsd),\n totalTokens: point.totalTokens,\n }));\n}\n\n/**\n * Build a governance `DataProvider` from a fetch + a format step. Every provider follows the same\n * shape: resolve the read-model, run ONE query over the panel's range, then format the rows into the\n * panel's result shape. When the host hasn't bound the read-model, `fetch` is skipped and `format`\n * runs over `[]` — which is exactly the empty-but-valid shape each formatter already yields (0 for\n * totals, `[]` for segments/rows), so the degraded case needs no special-casing.\n */\nfunction governanceStatProvider<TRow>(\n name: string,\n fetch: (queries: AgentGovernanceQueries, range: GovernanceRange) => Promise<TRow[]>,\n format: (rows: TRow[]) => unknown,\n): DataProvider {\n return {\n name,\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n const rows = queries ? await fetch(queries, resolveRange(query)) : [];\n return format(rows);\n },\n };\n}\n\n/** stat → authoritative total spend (USD) over the range. */\nexport function agentSpendTotalProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.totalCost',\n (queries, range) => queries.spendByModel(range),\n (rows) => ({ value: totalCostUsd(rows) }),\n );\n}\n\n/** stat → authoritative total tokens (input + output) over the range. */\nexport function agentTokensTotalProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.totalTokens',\n (queries, range) => queries.spendByModel(range),\n (rows) => ({ value: totalTokens(rows) }),\n );\n}\n\n/** breakdown → spend share per model. */\nexport function agentSpendByModelProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.byModel',\n (queries, range) => queries.spendByModel(range),\n (rows) => ({ segments: toModelSpendSegments(rows) }),\n );\n}\n\n/** table → per-model requests / in+out tokens / cost. */\nexport function agentModelSpendTableProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.byModelTable',\n (queries, range) => queries.spendByModel(range),\n (rows) => ({ rows: toModelSpendRows(rows) }),\n );\n}\n\n/** timeseries → daily spend + tokens trend. */\nexport function agentUsageTrendProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.usage.trend',\n (queries, range) => queries.usageTrend(range),\n (points) => ({ rows: toUsageTrendRows(points) }),\n );\n}\n\n/** table → spend per acting ref (user/tenant). */\nexport function agentActorSpendTableProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.byActor',\n (queries, range) => queries.spendByActor(range),\n (rows) => ({ rows: toActorSpendRows(rows) }),\n );\n}\n\n/** breakdown → spend share per actor. */\nexport function agentSpendByActorProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.byActorShare',\n (queries, range) => queries.spendByActor(range),\n (rows) => ({ segments: toActorSpendSegments(rows) }),\n );\n}\n\n/** table → top threads by cost (title, actor, requests, tokens, cost). */\nexport function agentTopThreadsTableProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.threads.topSpend',\n (queries, range) => queries.spendByThread(range, TOP_THREADS_LIMIT),\n (rows) => ({ rows: toThreadSpendRows(rows) }),\n );\n}\n","import { subscribe } from 'node:diagnostics_channel';\nimport { channelName } from '@dudousxd/nestjs-diagnostics';\nimport type { Watcher, WatcherContext } from '@dudousxd/nestjs-telescope';\n\nconst AGENT_EVENTS = ['run.started', 'message', 'tool-call', 'quota.exceeded', 'run.finished'];\n\ninterface DiagnosticEnvelope {\n event: string;\n payload: Record<string, unknown>;\n}\n\n/**\n * Records `aviary:agent:*` diagnostics events as Telescope entries of type `agent`. It depends\n * only on the diagnostics channel — not on the agent runtime — so it stays fully decoupled.\n */\nexport class AgentTelescopeWatcher implements Watcher {\n readonly type = 'agent';\n\n register(ctx: WatcherContext): void {\n for (const event of AGENT_EVENTS) {\n subscribe(channelName('agent', event), (message) => {\n const envelope = message as DiagnosticEnvelope;\n ctx.record({\n type: 'agent',\n content: { event: envelope.event, ...envelope.payload },\n tags: [envelope.event],\n });\n });\n }\n }\n}\n"],"mappings":"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","names":["import_nestjs_telescope","agentDashboard","id","label","panels","sections","title","cols","kind","data","provider","format","style","series","columns","key","fetchEntries","ctx","storage","moduleRef","get","TELESCOPE_STORAGE","strict","page","type","limit","data","ofEvent","entries","event","filter","entry","content","agentRunsProvider","name","resolve","_query","finished","value","length","agentTokensProvider","reduce","sum","inputTokens","outputTokens","agentToolsProvider","calls","slice","reverse","map","toolName","toolType","status","runId","rows","agentToolStatusProvider","counts","Map","call","set","segments","label","DEFAULT_TREND_WINDOW_DAYS","ISO_DAY_PATTERN","TOP_THREADS_LIMIT","isRecord","value","isGovernanceQueries","spendByModel","spendByActor","spendByThread","usageTrend","recentToolCalls","recentThreads","resolveGovernanceQueries","ctx","resolved","moduleRef","get","AGENT_GOVERNANCE_QUERIES","strict","isIsoDay","test","todayUtcDay","Date","toISOString","slice","shiftUtcDay","day","deltaDays","shifted","setUTCDate","getUTCDate","resolveRange","query","toDay","fromDay","roundCents","Math","round","totalCostUsd","rows","reduce","sum","row","costUsd","totalTokens","inputTokens","outputTokens","toModelSpendSegments","filter","map","label","modelId","toModelSpendRows","requests","toActorSpendRows","actorRef","toThreadSpendRows","title","threadId","toActorSpendSegments","toUsageTrendRows","points","point","governanceStatProvider","name","fetch","format","resolve","queries","agentSpendTotalProvider","range","agentTokensTotalProvider","agentSpendByModelProvider","segments","agentModelSpendTableProvider","agentUsageTrendProvider","agentActorSpendTableProvider","agentSpendByActorProvider","agentTopThreadsTableProvider","AGENT_EVENTS","AgentTelescopeWatcher","type","register","ctx","event","subscribe","channelName","message","envelope","record","content","payload","tags","agentTelescopeExtension","defineTelescopeExtension","name","watchers","AgentTelescopeWatcher","entryTypes","id","label","dot","dashboards","agentDashboard","dataProviders","agentRunsProvider","agentTokensProvider","agentToolsProvider","agentToolStatusProvider","agentSpendTotalProvider","agentTokensTotalProvider","agentSpendByModelProvider","agentModelSpendTableProvider","agentUsageTrendProvider","agentActorSpendTableProvider","agentSpendByActorProvider","agentTopThreadsTableProvider"]}
package/dist/index.js CHANGED
@@ -146,6 +146,40 @@ function agentDashboard() {
146
146
  }
147
147
  ]
148
148
  },
149
+ {
150
+ title: "Threads",
151
+ panels: [
152
+ {
153
+ kind: "table",
154
+ title: "Top threads by cost",
155
+ data: {
156
+ provider: "agent.threads.topSpend"
157
+ },
158
+ columns: [
159
+ {
160
+ key: "title",
161
+ label: "Thread"
162
+ },
163
+ {
164
+ key: "actorRef",
165
+ label: "Actor"
166
+ },
167
+ {
168
+ key: "requests",
169
+ label: "Requests"
170
+ },
171
+ {
172
+ key: "totalTokens",
173
+ label: "Tokens"
174
+ },
175
+ {
176
+ key: "costUsd",
177
+ label: "Cost (USD)"
178
+ }
179
+ ]
180
+ }
181
+ ]
182
+ },
149
183
  {
150
184
  title: "Tools",
151
185
  cols: 2,
@@ -276,12 +310,13 @@ __name(agentToolStatusProvider, "agentToolStatusProvider");
276
310
  import { AGENT_GOVERNANCE_QUERIES } from "@dudousxd/nestjs-agent-core";
277
311
  var DEFAULT_TREND_WINDOW_DAYS = 30;
278
312
  var ISO_DAY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
313
+ var TOP_THREADS_LIMIT = 10;
279
314
  function isRecord(value) {
280
315
  return typeof value === "object" && value !== null;
281
316
  }
282
317
  __name(isRecord, "isRecord");
283
318
  function isGovernanceQueries(value) {
284
- return isRecord(value) && typeof value.spendByModel === "function" && typeof value.spendByActor === "function" && typeof value.usageTrend === "function" && typeof value.recentToolCalls === "function" && typeof value.recentThreads === "function";
319
+ return isRecord(value) && typeof value.spendByModel === "function" && typeof value.spendByActor === "function" && typeof value.spendByThread === "function" && typeof value.usageTrend === "function" && typeof value.recentToolCalls === "function" && typeof value.recentThreads === "function";
285
320
  }
286
321
  __name(isGovernanceQueries, "isGovernanceQueries");
287
322
  function resolveGovernanceQueries(ctx) {
@@ -357,6 +392,16 @@ function toActorSpendRows(rows) {
357
392
  }));
358
393
  }
359
394
  __name(toActorSpendRows, "toActorSpendRows");
395
+ function toThreadSpendRows(rows) {
396
+ return rows.map((row) => ({
397
+ title: row.title || row.threadId,
398
+ actorRef: row.actorRef,
399
+ requests: row.requests,
400
+ totalTokens: row.totalTokens,
401
+ costUsd: roundCents(row.costUsd)
402
+ }));
403
+ }
404
+ __name(toThreadSpendRows, "toThreadSpendRows");
360
405
  function toActorSpendSegments(rows) {
361
406
  return rows.filter((row) => row.costUsd > 0).map((row) => ({
362
407
  label: row.actorRef,
@@ -372,132 +417,65 @@ function toUsageTrendRows(points) {
372
417
  }));
373
418
  }
374
419
  __name(toUsageTrendRows, "toUsageTrendRows");
375
- function agentSpendTotalProvider() {
420
+ function governanceStatProvider(name, fetch, format) {
376
421
  return {
377
- name: "agent.spend.totalCost",
422
+ name,
378
423
  async resolve(query, ctx) {
379
424
  const queries = resolveGovernanceQueries(ctx);
380
- if (!queries) {
381
- return {
382
- value: 0
383
- };
384
- }
385
- const byModel = await queries.spendByModel(resolveRange(query));
386
- return {
387
- value: totalCostUsd(byModel)
388
- };
425
+ const rows = queries ? await fetch(queries, resolveRange(query)) : [];
426
+ return format(rows);
389
427
  }
390
428
  };
391
429
  }
430
+ __name(governanceStatProvider, "governanceStatProvider");
431
+ function agentSpendTotalProvider() {
432
+ return governanceStatProvider("agent.spend.totalCost", (queries, range) => queries.spendByModel(range), (rows) => ({
433
+ value: totalCostUsd(rows)
434
+ }));
435
+ }
392
436
  __name(agentSpendTotalProvider, "agentSpendTotalProvider");
393
437
  function agentTokensTotalProvider() {
394
- return {
395
- name: "agent.spend.totalTokens",
396
- async resolve(query, ctx) {
397
- const queries = resolveGovernanceQueries(ctx);
398
- if (!queries) {
399
- return {
400
- value: 0
401
- };
402
- }
403
- const byModel = await queries.spendByModel(resolveRange(query));
404
- return {
405
- value: totalTokens(byModel)
406
- };
407
- }
408
- };
438
+ return governanceStatProvider("agent.spend.totalTokens", (queries, range) => queries.spendByModel(range), (rows) => ({
439
+ value: totalTokens(rows)
440
+ }));
409
441
  }
410
442
  __name(agentTokensTotalProvider, "agentTokensTotalProvider");
411
443
  function agentSpendByModelProvider() {
412
- return {
413
- name: "agent.spend.byModel",
414
- async resolve(query, ctx) {
415
- const queries = resolveGovernanceQueries(ctx);
416
- if (!queries) {
417
- return {
418
- segments: []
419
- };
420
- }
421
- const byModel = await queries.spendByModel(resolveRange(query));
422
- return {
423
- segments: toModelSpendSegments(byModel)
424
- };
425
- }
426
- };
444
+ return governanceStatProvider("agent.spend.byModel", (queries, range) => queries.spendByModel(range), (rows) => ({
445
+ segments: toModelSpendSegments(rows)
446
+ }));
427
447
  }
428
448
  __name(agentSpendByModelProvider, "agentSpendByModelProvider");
429
449
  function agentModelSpendTableProvider() {
430
- return {
431
- name: "agent.spend.byModelTable",
432
- async resolve(query, ctx) {
433
- const queries = resolveGovernanceQueries(ctx);
434
- if (!queries) {
435
- return {
436
- rows: []
437
- };
438
- }
439
- const byModel = await queries.spendByModel(resolveRange(query));
440
- return {
441
- rows: toModelSpendRows(byModel)
442
- };
443
- }
444
- };
450
+ return governanceStatProvider("agent.spend.byModelTable", (queries, range) => queries.spendByModel(range), (rows) => ({
451
+ rows: toModelSpendRows(rows)
452
+ }));
445
453
  }
446
454
  __name(agentModelSpendTableProvider, "agentModelSpendTableProvider");
447
455
  function agentUsageTrendProvider() {
448
- return {
449
- name: "agent.usage.trend",
450
- async resolve(query, ctx) {
451
- const queries = resolveGovernanceQueries(ctx);
452
- if (!queries) {
453
- return {
454
- rows: []
455
- };
456
- }
457
- const trend = await queries.usageTrend(resolveRange(query));
458
- return {
459
- rows: toUsageTrendRows(trend)
460
- };
461
- }
462
- };
456
+ return governanceStatProvider("agent.usage.trend", (queries, range) => queries.usageTrend(range), (points) => ({
457
+ rows: toUsageTrendRows(points)
458
+ }));
463
459
  }
464
460
  __name(agentUsageTrendProvider, "agentUsageTrendProvider");
465
461
  function agentActorSpendTableProvider() {
466
- return {
467
- name: "agent.spend.byActor",
468
- async resolve(query, ctx) {
469
- const queries = resolveGovernanceQueries(ctx);
470
- if (!queries) {
471
- return {
472
- rows: []
473
- };
474
- }
475
- const byActor = await queries.spendByActor(resolveRange(query));
476
- return {
477
- rows: toActorSpendRows(byActor)
478
- };
479
- }
480
- };
462
+ return governanceStatProvider("agent.spend.byActor", (queries, range) => queries.spendByActor(range), (rows) => ({
463
+ rows: toActorSpendRows(rows)
464
+ }));
481
465
  }
482
466
  __name(agentActorSpendTableProvider, "agentActorSpendTableProvider");
483
467
  function agentSpendByActorProvider() {
484
- return {
485
- name: "agent.spend.byActorShare",
486
- async resolve(query, ctx) {
487
- const queries = resolveGovernanceQueries(ctx);
488
- if (!queries) {
489
- return {
490
- segments: []
491
- };
492
- }
493
- const byActor = await queries.spendByActor(resolveRange(query));
494
- return {
495
- segments: toActorSpendSegments(byActor)
496
- };
497
- }
498
- };
468
+ return governanceStatProvider("agent.spend.byActorShare", (queries, range) => queries.spendByActor(range), (rows) => ({
469
+ segments: toActorSpendSegments(rows)
470
+ }));
499
471
  }
500
472
  __name(agentSpendByActorProvider, "agentSpendByActorProvider");
473
+ function agentTopThreadsTableProvider() {
474
+ return governanceStatProvider("agent.threads.topSpend", (queries, range) => queries.spendByThread(range, TOP_THREADS_LIMIT), (rows) => ({
475
+ rows: toThreadSpendRows(rows)
476
+ }));
477
+ }
478
+ __name(agentTopThreadsTableProvider, "agentTopThreadsTableProvider");
501
479
 
502
480
  // src/agent-telescope.watcher.ts
503
481
  import { subscribe } from "node:diagnostics_channel";
@@ -561,7 +539,8 @@ function agentTelescopeExtension() {
561
539
  agentModelSpendTableProvider(),
562
540
  agentUsageTrendProvider(),
563
541
  agentActorSpendTableProvider(),
564
- agentSpendByActorProvider()
542
+ agentSpendByActorProvider(),
543
+ agentTopThreadsTableProvider()
565
544
  ], "dataProviders")
566
545
  });
567
546
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/agent-telescope.extension.ts","../src/agent-dashboard.ts","../src/agent-data-providers.ts","../src/agent-governance-providers.ts","../src/agent-telescope.watcher.ts"],"sourcesContent":["import { defineTelescopeExtension } from '@dudousxd/nestjs-telescope';\nimport { agentDashboard } from './agent-dashboard.js';\nimport {\n agentRunsProvider,\n agentTokensProvider,\n agentToolStatusProvider,\n agentToolsProvider,\n} from './agent-data-providers.js';\nimport {\n agentActorSpendTableProvider,\n agentModelSpendTableProvider,\n agentSpendByActorProvider,\n agentSpendByModelProvider,\n agentSpendTotalProvider,\n agentTokensTotalProvider,\n agentUsageTrendProvider,\n} from './agent-governance-providers.js';\nimport { AgentTelescopeWatcher } from './agent-telescope.watcher.js';\n\n/**\n * The first-class Telescope extension for nestjs-agent: an \"Agent\" tab fed by two sources —\n * the `aviary:agent:*` diagnostics channel (live runs, tool calls) via the watcher, and the\n * authoritative `AGENT_GOVERNANCE_QUERIES` read-model (historical spend/usage) via the governance\n * providers. The extension `name`, entry-type id, dashboard id, and every provider name share the\n * `agent` prefix so the registry's global-uniqueness namespaces never collide with sibling extensions.\n *\n * Host wiring: the governance (Spend/Models/Actors) panels resolve `AGENT_GOVERNANCE_QUERIES` from\n * the host DI container at request time (via `ctx.moduleRef`). The host must bind that token — from\n * its store adapter (e.g. `store-mikro-orm` / `store-drizzle` / `testing`) — in the same module that\n * registers `TelescopeModule.forRoot({ extensions: [agentTelescopeExtension()] })`. If the binding is\n * absent, those panels render an empty state; the live watcher-fed panels keep working regardless.\n */\nexport function agentTelescopeExtension() {\n return defineTelescopeExtension({\n name: 'agent',\n watchers: () => [new AgentTelescopeWatcher()],\n entryTypes: () => [{ id: 'agent', label: 'Agent', dot: 'bg-violet-400' }],\n dashboards: () => [agentDashboard()],\n dataProviders: () => [\n agentRunsProvider(),\n agentTokensProvider(),\n agentToolsProvider(),\n agentToolStatusProvider(),\n agentSpendTotalProvider(),\n agentTokensTotalProvider(),\n agentSpendByModelProvider(),\n agentModelSpendTableProvider(),\n agentUsageTrendProvider(),\n agentActorSpendTableProvider(),\n agentSpendByActorProvider(),\n ],\n });\n}\n","import type { DashboardSpec } from '@dudousxd/nestjs-telescope';\n\n/** The \"Agent\" overview dashboard. Panels bind to the `agent.*` data providers. */\nexport function agentDashboard(): DashboardSpec {\n return {\n id: 'agent.overview',\n label: 'Agent',\n panels: [],\n sections: [\n {\n title: 'Overview',\n cols: 2,\n panels: [\n { kind: 'stat', title: 'Runs', data: { provider: 'agent.runs' } },\n { kind: 'stat', title: 'Tokens', data: { provider: 'agent.tokens' } },\n ],\n },\n {\n title: 'Spend',\n cols: 2,\n panels: [\n {\n kind: 'stat',\n title: 'Total spend (USD)',\n data: { provider: 'agent.spend.totalCost' },\n format: 'number',\n },\n {\n kind: 'stat',\n title: 'Total tokens',\n data: { provider: 'agent.spend.totalTokens' },\n format: 'number',\n },\n {\n kind: 'breakdown',\n title: 'Spend by model',\n data: { provider: 'agent.spend.byModel' },\n style: 'donut',\n },\n {\n kind: 'timeseries',\n title: 'Daily spend & tokens',\n data: { provider: 'agent.usage.trend' },\n series: ['costUsd', 'totalTokens'],\n style: 'area',\n },\n ],\n },\n {\n title: 'Models',\n panels: [\n {\n kind: 'table',\n title: 'Usage & cost by model',\n data: { provider: 'agent.spend.byModelTable' },\n columns: [\n { key: 'modelId', label: 'Model' },\n { key: 'requests', label: 'Requests' },\n { key: 'inputTokens', label: 'Input tokens' },\n { key: 'outputTokens', label: 'Output tokens' },\n { key: 'costUsd', label: 'Cost (USD)' },\n ],\n },\n ],\n },\n {\n title: 'Actors',\n cols: 2,\n panels: [\n {\n kind: 'breakdown',\n title: 'Spend share by actor',\n data: { provider: 'agent.spend.byActorShare' },\n style: 'bar',\n },\n {\n kind: 'table',\n title: 'Spend by actor',\n data: { provider: 'agent.spend.byActor' },\n columns: [\n { key: 'actorRef', label: 'Actor' },\n { key: 'requests', label: 'Requests' },\n { key: 'totalTokens', label: 'Tokens' },\n { key: 'costUsd', label: 'Cost (USD)' },\n ],\n },\n ],\n },\n {\n title: 'Tools',\n cols: 2,\n panels: [\n {\n kind: 'breakdown',\n title: 'Tool-call status',\n data: { provider: 'agent.toolStatus' },\n style: 'donut',\n },\n {\n kind: 'table',\n title: 'Recent tool calls',\n data: { provider: 'agent.tools' },\n columns: [\n { key: 'toolName', label: 'Tool' },\n { key: 'toolType', label: 'Type' },\n { key: 'status', label: 'Status' },\n { key: 'runId', label: 'Run' },\n ],\n },\n ],\n },\n ],\n };\n}\n","import type { DataProvider, ExtensionContext } from '@dudousxd/nestjs-telescope';\nimport { TELESCOPE_STORAGE } from '@dudousxd/nestjs-telescope';\n\ninterface AgentEntryContent {\n event: string;\n runId?: string;\n threadId?: string;\n toolName?: string;\n toolType?: string;\n status?: string;\n steps?: number;\n inputTokens?: number;\n outputTokens?: number;\n}\n\ninterface StorageEntry {\n content?: AgentEntryContent;\n createdAt?: Date;\n}\n\nasync function fetchEntries(ctx: ExtensionContext): Promise<StorageEntry[]> {\n const storage = ctx.moduleRef.get(TELESCOPE_STORAGE, { strict: false }) as {\n get(query: { type?: string; limit?: number }): Promise<{ data: StorageEntry[] }>;\n };\n const page = await storage.get({ type: 'agent', limit: 5_000 });\n return page.data;\n}\n\nfunction ofEvent(entries: StorageEntry[], event: string): StorageEntry[] {\n return entries.filter((entry) => entry.content?.event === event);\n}\n\n/** stat → total agent runs (run.finished entries). */\nexport function agentRunsProvider(): DataProvider {\n return {\n name: 'agent.runs',\n async resolve(_query, ctx) {\n const finished = ofEvent(await fetchEntries(ctx), 'run.finished');\n return { value: finished.length };\n },\n };\n}\n\n/** stat → total tokens across all finished runs. */\nexport function agentTokensProvider(): DataProvider {\n return {\n name: 'agent.tokens',\n async resolve(_query, ctx) {\n const finished = ofEvent(await fetchEntries(ctx), 'run.finished');\n const value = finished.reduce(\n (sum, entry) =>\n sum + (entry.content?.inputTokens ?? 0) + (entry.content?.outputTokens ?? 0),\n 0,\n );\n return { value };\n },\n };\n}\n\n/** table → recent tool calls. */\nexport function agentToolsProvider(): DataProvider {\n return {\n name: 'agent.tools',\n async resolve(_query, ctx) {\n const calls = ofEvent(await fetchEntries(ctx), 'tool-call')\n .slice(-50)\n .reverse()\n .map((entry) => ({\n toolName: entry.content?.toolName ?? '',\n toolType: entry.content?.toolType ?? '',\n status: entry.content?.status ?? '',\n runId: entry.content?.runId ?? '',\n }));\n return { rows: calls };\n },\n };\n}\n\n/** breakdown → tool-call status distribution. */\nexport function agentToolStatusProvider(): DataProvider {\n return {\n name: 'agent.toolStatus',\n async resolve(_query, ctx) {\n const calls = ofEvent(await fetchEntries(ctx), 'tool-call');\n const counts = new Map<string, number>();\n for (const call of calls) {\n const status = call.content?.status ?? 'unknown';\n counts.set(status, (counts.get(status) ?? 0) + 1);\n }\n return { segments: [...counts.entries()].map(([label, value]) => ({ label, value })) };\n },\n };\n}\n","import type {\n ActorSpendRow,\n AgentGovernanceQueries,\n GovernanceRange,\n ModelSpendRow,\n UsageTrendPoint,\n} from '@dudousxd/nestjs-agent-core';\nimport { AGENT_GOVERNANCE_QUERIES } from '@dudousxd/nestjs-agent-core';\nimport type { DataProvider, ExtensionContext } from '@dudousxd/nestjs-telescope';\n\n/**\n * Governance data providers for the \"Agent\" Telescope tab. Unlike the live watcher-fed providers\n * in `agent-data-providers.ts` (which read the ephemeral Telescope event storage), these read the\n * authoritative, restart-surviving read-model: `AGENT_GOVERNANCE_QUERIES` (usage ⋈ pricing).\n *\n * The read-model is resolved from the host's DI container via `ctx.moduleRef` — the exact same\n * mechanism the existing providers use for `TELESCOPE_STORAGE`. The host must bind\n * `AGENT_GOVERNANCE_QUERIES` (from its store adapter) in the same module that registers Telescope;\n * when the binding is absent every provider degrades to an empty-but-valid shape rather than throwing.\n */\n\n/** Default trailing window (in days, inclusive) when a panel query omits an explicit range. */\nconst DEFAULT_TREND_WINDOW_DAYS = 30;\n\nconst ISO_DAY_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** A breakdown-panel segment (Telescope core: `{ segments: Array<{ label, value, color? }> }`). */\ninterface BreakdownSegment {\n label: string;\n value: number;\n}\n\n/** One row of the per-model usage/cost table. */\ninterface ModelSpendTableRow {\n modelId: string;\n requests: number;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n}\n\n/** One row of the per-actor spend table. */\ninterface ActorSpendTableRow {\n actorRef: string;\n requests: number;\n totalTokens: number;\n costUsd: number;\n}\n\n/** One point of the timeseries trend (Telescope core: `{ label } & Record<string, number>`). */\ninterface UsageTrendTableRow {\n label: string;\n costUsd: number;\n totalTokens: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Structural guard so the DI-resolved binding is narrowed without an `as`/`any` cast. */\nfunction isGovernanceQueries(value: unknown): value is AgentGovernanceQueries {\n return (\n isRecord(value) &&\n typeof value.spendByModel === 'function' &&\n typeof value.spendByActor === 'function' &&\n typeof value.usageTrend === 'function' &&\n typeof value.recentToolCalls === 'function' &&\n typeof value.recentThreads === 'function'\n );\n}\n\n/**\n * Resolve the governance read-model from the host container. Returns `null` when the host has not\n * bound `AGENT_GOVERNANCE_QUERIES` (missing token throws inside `moduleRef.get`) so panels can render\n * an empty state instead of erroring.\n */\nfunction resolveGovernanceQueries(ctx: ExtensionContext): AgentGovernanceQueries | null {\n let resolved: unknown;\n try {\n resolved = ctx.moduleRef.get(AGENT_GOVERNANCE_QUERIES, { strict: false });\n } catch {\n return null;\n }\n return isGovernanceQueries(resolved) ? resolved : null;\n}\n\nfunction isIsoDay(value: unknown): value is string {\n return typeof value === 'string' && ISO_DAY_PATTERN.test(value);\n}\n\nfunction todayUtcDay(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\n/** Shift a `YYYY-MM-DD` UTC day by `deltaDays` (negative goes back in time). */\nexport function shiftUtcDay(day: string, deltaDays: number): string {\n const shifted = new Date(`${day}T00:00:00.000Z`);\n shifted.setUTCDate(shifted.getUTCDate() + deltaDays);\n return shifted.toISOString().slice(0, 10);\n}\n\n/**\n * Derive the query range: honour explicit `fromDay`/`toDay` (validated ISO days), otherwise default\n * to the trailing {@link DEFAULT_TREND_WINDOW_DAYS}-day window ending today (UTC).\n */\nexport function resolveRange(query: Record<string, unknown> | undefined): GovernanceRange {\n const toDay = query && isIsoDay(query.toDay) ? query.toDay : todayUtcDay();\n const fromDay =\n query && isIsoDay(query.fromDay)\n ? query.fromDay\n : shiftUtcDay(toDay, -(DEFAULT_TREND_WINDOW_DAYS - 1));\n return { fromDay, toDay };\n}\n\nfunction roundCents(value: number): number {\n return Math.round(value * 100) / 100;\n}\n\n/** Sum authoritative spend across every model row. */\nexport function totalCostUsd(rows: ModelSpendRow[]): number {\n return roundCents(rows.reduce((sum, row) => sum + row.costUsd, 0));\n}\n\n/** Sum input + output tokens across every model row. */\nexport function totalTokens(rows: ModelSpendRow[]): number {\n return rows.reduce((sum, row) => sum + row.inputTokens + row.outputTokens, 0);\n}\n\n/** Spend-by-model as breakdown segments (models with zero cost are dropped from the donut). */\nexport function toModelSpendSegments(rows: ModelSpendRow[]): BreakdownSegment[] {\n return rows\n .filter((row) => row.costUsd > 0)\n .map((row) => ({ label: row.modelId, value: roundCents(row.costUsd) }));\n}\n\n/** Spend-by-model as table rows (cost rounded to cents; usage kept exact). */\nexport function toModelSpendRows(rows: ModelSpendRow[]): ModelSpendTableRow[] {\n return rows.map((row) => ({\n modelId: row.modelId,\n requests: row.requests,\n inputTokens: row.inputTokens,\n outputTokens: row.outputTokens,\n costUsd: roundCents(row.costUsd),\n }));\n}\n\n/** Spend-by-actor as table rows. */\nexport function toActorSpendRows(rows: ActorSpendRow[]): ActorSpendTableRow[] {\n return rows.map((row) => ({\n actorRef: row.actorRef,\n requests: row.requests,\n totalTokens: row.totalTokens,\n costUsd: roundCents(row.costUsd),\n }));\n}\n\n/** Spend-by-actor as breakdown segments (actors with zero cost are dropped). */\nexport function toActorSpendSegments(rows: ActorSpendRow[]): BreakdownSegment[] {\n return rows\n .filter((row) => row.costUsd > 0)\n .map((row) => ({ label: row.actorRef, value: roundCents(row.costUsd) }));\n}\n\n/** Daily usage trend as timeseries rows keyed by `label` (the day). */\nexport function toUsageTrendRows(points: UsageTrendPoint[]): UsageTrendTableRow[] {\n return points.map((point) => ({\n label: point.day,\n costUsd: roundCents(point.costUsd),\n totalTokens: point.totalTokens,\n }));\n}\n\n/** stat → authoritative total spend (USD) over the range. */\nexport function agentSpendTotalProvider(): DataProvider {\n return {\n name: 'agent.spend.totalCost',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { value: 0 };\n }\n const byModel = await queries.spendByModel(resolveRange(query));\n return { value: totalCostUsd(byModel) };\n },\n };\n}\n\n/** stat → authoritative total tokens (input + output) over the range. */\nexport function agentTokensTotalProvider(): DataProvider {\n return {\n name: 'agent.spend.totalTokens',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { value: 0 };\n }\n const byModel = await queries.spendByModel(resolveRange(query));\n return { value: totalTokens(byModel) };\n },\n };\n}\n\n/** breakdown → spend share per model. */\nexport function agentSpendByModelProvider(): DataProvider {\n return {\n name: 'agent.spend.byModel',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { segments: [] };\n }\n const byModel = await queries.spendByModel(resolveRange(query));\n return { segments: toModelSpendSegments(byModel) };\n },\n };\n}\n\n/** table → per-model requests / in+out tokens / cost. */\nexport function agentModelSpendTableProvider(): DataProvider {\n return {\n name: 'agent.spend.byModelTable',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { rows: [] };\n }\n const byModel = await queries.spendByModel(resolveRange(query));\n return { rows: toModelSpendRows(byModel) };\n },\n };\n}\n\n/** timeseries → daily spend + tokens trend. */\nexport function agentUsageTrendProvider(): DataProvider {\n return {\n name: 'agent.usage.trend',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { rows: [] };\n }\n const trend = await queries.usageTrend(resolveRange(query));\n return { rows: toUsageTrendRows(trend) };\n },\n };\n}\n\n/** table → spend per acting ref (user/tenant). */\nexport function agentActorSpendTableProvider(): DataProvider {\n return {\n name: 'agent.spend.byActor',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { rows: [] };\n }\n const byActor = await queries.spendByActor(resolveRange(query));\n return { rows: toActorSpendRows(byActor) };\n },\n };\n}\n\n/** breakdown → spend share per actor. */\nexport function agentSpendByActorProvider(): DataProvider {\n return {\n name: 'agent.spend.byActorShare',\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n if (!queries) {\n return { segments: [] };\n }\n const byActor = await queries.spendByActor(resolveRange(query));\n return { segments: toActorSpendSegments(byActor) };\n },\n };\n}\n","import { subscribe } from 'node:diagnostics_channel';\nimport { channelName } from '@dudousxd/nestjs-diagnostics';\nimport type { Watcher, WatcherContext } from '@dudousxd/nestjs-telescope';\n\nconst AGENT_EVENTS = ['run.started', 'message', 'tool-call', 'quota.exceeded', 'run.finished'];\n\ninterface DiagnosticEnvelope {\n event: string;\n payload: Record<string, unknown>;\n}\n\n/**\n * Records `aviary:agent:*` diagnostics events as Telescope entries of type `agent`. It depends\n * only on the diagnostics channel — not on the agent runtime — so it stays fully decoupled.\n */\nexport class AgentTelescopeWatcher implements Watcher {\n readonly type = 'agent';\n\n register(ctx: WatcherContext): void {\n for (const event of AGENT_EVENTS) {\n subscribe(channelName('agent', event), (message) => {\n const envelope = message as DiagnosticEnvelope;\n ctx.record({\n type: 'agent',\n content: { event: envelope.event, ...envelope.payload },\n tags: [envelope.event],\n });\n });\n }\n }\n}\n"],"mappings":"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","names":["defineTelescopeExtension","agentDashboard","id","label","panels","sections","title","cols","kind","data","provider","format","style","series","columns","key","TELESCOPE_STORAGE","fetchEntries","ctx","storage","moduleRef","get","TELESCOPE_STORAGE","strict","page","type","limit","data","ofEvent","entries","event","filter","entry","content","agentRunsProvider","name","resolve","_query","finished","value","length","agentTokensProvider","reduce","sum","inputTokens","outputTokens","agentToolsProvider","calls","slice","reverse","map","toolName","toolType","status","runId","rows","agentToolStatusProvider","counts","Map","call","set","segments","label","AGENT_GOVERNANCE_QUERIES","DEFAULT_TREND_WINDOW_DAYS","ISO_DAY_PATTERN","isRecord","value","isGovernanceQueries","spendByModel","spendByActor","usageTrend","recentToolCalls","recentThreads","resolveGovernanceQueries","ctx","resolved","moduleRef","get","AGENT_GOVERNANCE_QUERIES","strict","isIsoDay","test","todayUtcDay","Date","toISOString","slice","shiftUtcDay","day","deltaDays","shifted","setUTCDate","getUTCDate","resolveRange","query","toDay","fromDay","roundCents","Math","round","totalCostUsd","rows","reduce","sum","row","costUsd","totalTokens","inputTokens","outputTokens","toModelSpendSegments","filter","map","label","modelId","toModelSpendRows","requests","toActorSpendRows","actorRef","toActorSpendSegments","toUsageTrendRows","points","point","agentSpendTotalProvider","name","resolve","queries","byModel","agentTokensTotalProvider","agentSpendByModelProvider","segments","agentModelSpendTableProvider","agentUsageTrendProvider","trend","agentActorSpendTableProvider","byActor","agentSpendByActorProvider","subscribe","channelName","AGENT_EVENTS","AgentTelescopeWatcher","type","register","ctx","event","subscribe","channelName","message","envelope","record","content","payload","tags","agentTelescopeExtension","defineTelescopeExtension","name","watchers","AgentTelescopeWatcher","entryTypes","id","label","dot","dashboards","agentDashboard","dataProviders","agentRunsProvider","agentTokensProvider","agentToolsProvider","agentToolStatusProvider","agentSpendTotalProvider","agentTokensTotalProvider","agentSpendByModelProvider","agentModelSpendTableProvider","agentUsageTrendProvider","agentActorSpendTableProvider","agentSpendByActorProvider"]}
1
+ {"version":3,"sources":["../src/agent-telescope.extension.ts","../src/agent-dashboard.ts","../src/agent-data-providers.ts","../src/agent-governance-providers.ts","../src/agent-telescope.watcher.ts"],"sourcesContent":["import { defineTelescopeExtension } from '@dudousxd/nestjs-telescope';\nimport { agentDashboard } from './agent-dashboard.js';\nimport {\n agentRunsProvider,\n agentTokensProvider,\n agentToolStatusProvider,\n agentToolsProvider,\n} from './agent-data-providers.js';\nimport {\n agentActorSpendTableProvider,\n agentModelSpendTableProvider,\n agentSpendByActorProvider,\n agentSpendByModelProvider,\n agentSpendTotalProvider,\n agentTokensTotalProvider,\n agentTopThreadsTableProvider,\n agentUsageTrendProvider,\n} from './agent-governance-providers.js';\nimport { AgentTelescopeWatcher } from './agent-telescope.watcher.js';\n\n/**\n * The first-class Telescope extension for nestjs-agent: an \"Agent\" tab fed by two sources —\n * the `aviary:agent:*` diagnostics channel (live runs, tool calls) via the watcher, and the\n * authoritative `AGENT_GOVERNANCE_QUERIES` read-model (historical spend/usage) via the governance\n * providers. The extension `name`, entry-type id, dashboard id, and every provider name share the\n * `agent` prefix so the registry's global-uniqueness namespaces never collide with sibling extensions.\n *\n * Host wiring: the governance (Spend/Models/Actors) panels resolve `AGENT_GOVERNANCE_QUERIES` from\n * the host DI container at request time (via `ctx.moduleRef`). The host must bind that token — from\n * its store adapter (e.g. `store-mikro-orm` / `store-drizzle` / `testing`) — in the same module that\n * registers `TelescopeModule.forRoot({ extensions: [agentTelescopeExtension()] })`. If the binding is\n * absent, those panels render an empty state; the live watcher-fed panels keep working regardless.\n */\nexport function agentTelescopeExtension() {\n return defineTelescopeExtension({\n name: 'agent',\n watchers: () => [new AgentTelescopeWatcher()],\n entryTypes: () => [{ id: 'agent', label: 'Agent', dot: 'bg-violet-400' }],\n dashboards: () => [agentDashboard()],\n dataProviders: () => [\n agentRunsProvider(),\n agentTokensProvider(),\n agentToolsProvider(),\n agentToolStatusProvider(),\n agentSpendTotalProvider(),\n agentTokensTotalProvider(),\n agentSpendByModelProvider(),\n agentModelSpendTableProvider(),\n agentUsageTrendProvider(),\n agentActorSpendTableProvider(),\n agentSpendByActorProvider(),\n agentTopThreadsTableProvider(),\n ],\n });\n}\n","import type { DashboardSpec } from '@dudousxd/nestjs-telescope';\n\n/** The \"Agent\" overview dashboard. Panels bind to the `agent.*` data providers. */\nexport function agentDashboard(): DashboardSpec {\n return {\n id: 'agent.overview',\n label: 'Agent',\n panels: [],\n sections: [\n {\n title: 'Overview',\n cols: 2,\n panels: [\n { kind: 'stat', title: 'Runs', data: { provider: 'agent.runs' } },\n { kind: 'stat', title: 'Tokens', data: { provider: 'agent.tokens' } },\n ],\n },\n {\n title: 'Spend',\n cols: 2,\n panels: [\n {\n kind: 'stat',\n title: 'Total spend (USD)',\n data: { provider: 'agent.spend.totalCost' },\n format: 'number',\n },\n {\n kind: 'stat',\n title: 'Total tokens',\n data: { provider: 'agent.spend.totalTokens' },\n format: 'number',\n },\n {\n kind: 'breakdown',\n title: 'Spend by model',\n data: { provider: 'agent.spend.byModel' },\n style: 'donut',\n },\n {\n kind: 'timeseries',\n title: 'Daily spend & tokens',\n data: { provider: 'agent.usage.trend' },\n series: ['costUsd', 'totalTokens'],\n style: 'area',\n },\n ],\n },\n {\n title: 'Models',\n panels: [\n {\n kind: 'table',\n title: 'Usage & cost by model',\n data: { provider: 'agent.spend.byModelTable' },\n columns: [\n { key: 'modelId', label: 'Model' },\n { key: 'requests', label: 'Requests' },\n { key: 'inputTokens', label: 'Input tokens' },\n { key: 'outputTokens', label: 'Output tokens' },\n { key: 'costUsd', label: 'Cost (USD)' },\n ],\n },\n ],\n },\n {\n title: 'Actors',\n cols: 2,\n panels: [\n {\n kind: 'breakdown',\n title: 'Spend share by actor',\n data: { provider: 'agent.spend.byActorShare' },\n style: 'bar',\n },\n {\n kind: 'table',\n title: 'Spend by actor',\n data: { provider: 'agent.spend.byActor' },\n columns: [\n { key: 'actorRef', label: 'Actor' },\n { key: 'requests', label: 'Requests' },\n { key: 'totalTokens', label: 'Tokens' },\n { key: 'costUsd', label: 'Cost (USD)' },\n ],\n },\n ],\n },\n {\n title: 'Threads',\n panels: [\n {\n kind: 'table',\n title: 'Top threads by cost',\n data: { provider: 'agent.threads.topSpend' },\n columns: [\n { key: 'title', label: 'Thread' },\n { key: 'actorRef', label: 'Actor' },\n { key: 'requests', label: 'Requests' },\n { key: 'totalTokens', label: 'Tokens' },\n { key: 'costUsd', label: 'Cost (USD)' },\n ],\n },\n ],\n },\n {\n title: 'Tools',\n cols: 2,\n panels: [\n {\n kind: 'breakdown',\n title: 'Tool-call status',\n data: { provider: 'agent.toolStatus' },\n style: 'donut',\n },\n {\n kind: 'table',\n title: 'Recent tool calls',\n data: { provider: 'agent.tools' },\n columns: [\n { key: 'toolName', label: 'Tool' },\n { key: 'toolType', label: 'Type' },\n { key: 'status', label: 'Status' },\n { key: 'runId', label: 'Run' },\n ],\n },\n ],\n },\n ],\n };\n}\n","import type { DataProvider, ExtensionContext } from '@dudousxd/nestjs-telescope';\nimport { TELESCOPE_STORAGE } from '@dudousxd/nestjs-telescope';\n\ninterface AgentEntryContent {\n event: string;\n runId?: string;\n threadId?: string;\n toolName?: string;\n toolType?: string;\n status?: string;\n steps?: number;\n inputTokens?: number;\n outputTokens?: number;\n}\n\ninterface StorageEntry {\n content?: AgentEntryContent;\n createdAt?: Date;\n}\n\nasync function fetchEntries(ctx: ExtensionContext): Promise<StorageEntry[]> {\n const storage = ctx.moduleRef.get(TELESCOPE_STORAGE, { strict: false }) as {\n get(query: { type?: string; limit?: number }): Promise<{ data: StorageEntry[] }>;\n };\n const page = await storage.get({ type: 'agent', limit: 5_000 });\n return page.data;\n}\n\nfunction ofEvent(entries: StorageEntry[], event: string): StorageEntry[] {\n return entries.filter((entry) => entry.content?.event === event);\n}\n\n/** stat → total agent runs (run.finished entries). */\nexport function agentRunsProvider(): DataProvider {\n return {\n name: 'agent.runs',\n async resolve(_query, ctx) {\n const finished = ofEvent(await fetchEntries(ctx), 'run.finished');\n return { value: finished.length };\n },\n };\n}\n\n/** stat → total tokens across all finished runs. */\nexport function agentTokensProvider(): DataProvider {\n return {\n name: 'agent.tokens',\n async resolve(_query, ctx) {\n const finished = ofEvent(await fetchEntries(ctx), 'run.finished');\n const value = finished.reduce(\n (sum, entry) =>\n sum + (entry.content?.inputTokens ?? 0) + (entry.content?.outputTokens ?? 0),\n 0,\n );\n return { value };\n },\n };\n}\n\n/** table → recent tool calls. */\nexport function agentToolsProvider(): DataProvider {\n return {\n name: 'agent.tools',\n async resolve(_query, ctx) {\n const calls = ofEvent(await fetchEntries(ctx), 'tool-call')\n .slice(-50)\n .reverse()\n .map((entry) => ({\n toolName: entry.content?.toolName ?? '',\n toolType: entry.content?.toolType ?? '',\n status: entry.content?.status ?? '',\n runId: entry.content?.runId ?? '',\n }));\n return { rows: calls };\n },\n };\n}\n\n/** breakdown → tool-call status distribution. */\nexport function agentToolStatusProvider(): DataProvider {\n return {\n name: 'agent.toolStatus',\n async resolve(_query, ctx) {\n const calls = ofEvent(await fetchEntries(ctx), 'tool-call');\n const counts = new Map<string, number>();\n for (const call of calls) {\n const status = call.content?.status ?? 'unknown';\n counts.set(status, (counts.get(status) ?? 0) + 1);\n }\n return { segments: [...counts.entries()].map(([label, value]) => ({ label, value })) };\n },\n };\n}\n","import type {\n ActorSpendRow,\n AgentGovernanceQueries,\n GovernanceRange,\n ModelSpendRow,\n ThreadSpendRow,\n UsageTrendPoint,\n} from '@dudousxd/nestjs-agent-core';\nimport { AGENT_GOVERNANCE_QUERIES } from '@dudousxd/nestjs-agent-core';\nimport type { DataProvider, ExtensionContext } from '@dudousxd/nestjs-telescope';\n\n/**\n * Governance data providers for the \"Agent\" Telescope tab. Unlike the live watcher-fed providers\n * in `agent-data-providers.ts` (which read the ephemeral Telescope event storage), these read the\n * authoritative, restart-surviving read-model: `AGENT_GOVERNANCE_QUERIES` (usage ⋈ pricing).\n *\n * The read-model is resolved from the host's DI container via `ctx.moduleRef` — the exact same\n * mechanism the existing providers use for `TELESCOPE_STORAGE`. The host must bind\n * `AGENT_GOVERNANCE_QUERIES` (from its store adapter) in the same module that registers Telescope;\n * when the binding is absent every provider degrades to an empty-but-valid shape rather than throwing.\n */\n\n/** Default trailing window (in days, inclusive) when a panel query omits an explicit range. */\nconst DEFAULT_TREND_WINDOW_DAYS = 30;\n\nconst ISO_DAY_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** A breakdown-panel segment (Telescope core: `{ segments: Array<{ label, value, color? }> }`). */\ninterface BreakdownSegment {\n label: string;\n value: number;\n}\n\n/** One row of the per-model usage/cost table. */\ninterface ModelSpendTableRow {\n modelId: string;\n requests: number;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n}\n\n/** One row of the per-actor spend table. */\ninterface ActorSpendTableRow {\n actorRef: string;\n requests: number;\n totalTokens: number;\n costUsd: number;\n}\n\n/** One row of the top-threads-by-cost table. */\ninterface ThreadSpendTableRow {\n title: string;\n actorRef: string;\n requests: number;\n totalTokens: number;\n costUsd: number;\n}\n\n/** Top threads by cost within the range, capped to this count. */\nconst TOP_THREADS_LIMIT = 10;\n\n/** One point of the timeseries trend (Telescope core: `{ label } & Record<string, number>`). */\ninterface UsageTrendTableRow {\n label: string;\n costUsd: number;\n totalTokens: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** Structural guard so the DI-resolved binding is narrowed without an `as`/`any` cast. */\nfunction isGovernanceQueries(value: unknown): value is AgentGovernanceQueries {\n return (\n isRecord(value) &&\n typeof value.spendByModel === 'function' &&\n typeof value.spendByActor === 'function' &&\n typeof value.spendByThread === 'function' &&\n typeof value.usageTrend === 'function' &&\n typeof value.recentToolCalls === 'function' &&\n typeof value.recentThreads === 'function'\n );\n}\n\n/**\n * Resolve the governance read-model from the host container. Returns `null` when the host has not\n * bound `AGENT_GOVERNANCE_QUERIES` (missing token throws inside `moduleRef.get`) so panels can render\n * an empty state instead of erroring.\n */\nfunction resolveGovernanceQueries(ctx: ExtensionContext): AgentGovernanceQueries | null {\n let resolved: unknown;\n try {\n resolved = ctx.moduleRef.get(AGENT_GOVERNANCE_QUERIES, { strict: false });\n } catch {\n return null;\n }\n return isGovernanceQueries(resolved) ? resolved : null;\n}\n\nfunction isIsoDay(value: unknown): value is string {\n return typeof value === 'string' && ISO_DAY_PATTERN.test(value);\n}\n\nfunction todayUtcDay(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\n/** Shift a `YYYY-MM-DD` UTC day by `deltaDays` (negative goes back in time). */\nexport function shiftUtcDay(day: string, deltaDays: number): string {\n const shifted = new Date(`${day}T00:00:00.000Z`);\n shifted.setUTCDate(shifted.getUTCDate() + deltaDays);\n return shifted.toISOString().slice(0, 10);\n}\n\n/**\n * Derive the query range: honour explicit `fromDay`/`toDay` (validated ISO days), otherwise default\n * to the trailing {@link DEFAULT_TREND_WINDOW_DAYS}-day window ending today (UTC).\n */\nexport function resolveRange(query: Record<string, unknown> | undefined): GovernanceRange {\n const toDay = query && isIsoDay(query.toDay) ? query.toDay : todayUtcDay();\n const fromDay =\n query && isIsoDay(query.fromDay)\n ? query.fromDay\n : shiftUtcDay(toDay, -(DEFAULT_TREND_WINDOW_DAYS - 1));\n return { fromDay, toDay };\n}\n\nfunction roundCents(value: number): number {\n return Math.round(value * 100) / 100;\n}\n\n/** Sum authoritative spend across every model row. */\nexport function totalCostUsd(rows: ModelSpendRow[]): number {\n return roundCents(rows.reduce((sum, row) => sum + row.costUsd, 0));\n}\n\n/** Sum input + output tokens across every model row. */\nexport function totalTokens(rows: ModelSpendRow[]): number {\n return rows.reduce((sum, row) => sum + row.inputTokens + row.outputTokens, 0);\n}\n\n/** Spend-by-model as breakdown segments (models with zero cost are dropped from the donut). */\nexport function toModelSpendSegments(rows: ModelSpendRow[]): BreakdownSegment[] {\n return rows\n .filter((row) => row.costUsd > 0)\n .map((row) => ({ label: row.modelId, value: roundCents(row.costUsd) }));\n}\n\n/** Spend-by-model as table rows (cost rounded to cents; usage kept exact). */\nexport function toModelSpendRows(rows: ModelSpendRow[]): ModelSpendTableRow[] {\n return rows.map((row) => ({\n modelId: row.modelId,\n requests: row.requests,\n inputTokens: row.inputTokens,\n outputTokens: row.outputTokens,\n costUsd: roundCents(row.costUsd),\n }));\n}\n\n/** Spend-by-actor as table rows. */\nexport function toActorSpendRows(rows: ActorSpendRow[]): ActorSpendTableRow[] {\n return rows.map((row) => ({\n actorRef: row.actorRef,\n requests: row.requests,\n totalTokens: row.totalTokens,\n costUsd: roundCents(row.costUsd),\n }));\n}\n\n/** Top-threads-by-cost as table rows. */\nexport function toThreadSpendRows(rows: ThreadSpendRow[]): ThreadSpendTableRow[] {\n return rows.map((row) => ({\n title: row.title || row.threadId,\n actorRef: row.actorRef,\n requests: row.requests,\n totalTokens: row.totalTokens,\n costUsd: roundCents(row.costUsd),\n }));\n}\n\n/** Spend-by-actor as breakdown segments (actors with zero cost are dropped). */\nexport function toActorSpendSegments(rows: ActorSpendRow[]): BreakdownSegment[] {\n return rows\n .filter((row) => row.costUsd > 0)\n .map((row) => ({ label: row.actorRef, value: roundCents(row.costUsd) }));\n}\n\n/** Daily usage trend as timeseries rows keyed by `label` (the day). */\nexport function toUsageTrendRows(points: UsageTrendPoint[]): UsageTrendTableRow[] {\n return points.map((point) => ({\n label: point.day,\n costUsd: roundCents(point.costUsd),\n totalTokens: point.totalTokens,\n }));\n}\n\n/**\n * Build a governance `DataProvider` from a fetch + a format step. Every provider follows the same\n * shape: resolve the read-model, run ONE query over the panel's range, then format the rows into the\n * panel's result shape. When the host hasn't bound the read-model, `fetch` is skipped and `format`\n * runs over `[]` — which is exactly the empty-but-valid shape each formatter already yields (0 for\n * totals, `[]` for segments/rows), so the degraded case needs no special-casing.\n */\nfunction governanceStatProvider<TRow>(\n name: string,\n fetch: (queries: AgentGovernanceQueries, range: GovernanceRange) => Promise<TRow[]>,\n format: (rows: TRow[]) => unknown,\n): DataProvider {\n return {\n name,\n async resolve(query, ctx) {\n const queries = resolveGovernanceQueries(ctx);\n const rows = queries ? await fetch(queries, resolveRange(query)) : [];\n return format(rows);\n },\n };\n}\n\n/** stat → authoritative total spend (USD) over the range. */\nexport function agentSpendTotalProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.totalCost',\n (queries, range) => queries.spendByModel(range),\n (rows) => ({ value: totalCostUsd(rows) }),\n );\n}\n\n/** stat → authoritative total tokens (input + output) over the range. */\nexport function agentTokensTotalProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.totalTokens',\n (queries, range) => queries.spendByModel(range),\n (rows) => ({ value: totalTokens(rows) }),\n );\n}\n\n/** breakdown → spend share per model. */\nexport function agentSpendByModelProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.byModel',\n (queries, range) => queries.spendByModel(range),\n (rows) => ({ segments: toModelSpendSegments(rows) }),\n );\n}\n\n/** table → per-model requests / in+out tokens / cost. */\nexport function agentModelSpendTableProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.byModelTable',\n (queries, range) => queries.spendByModel(range),\n (rows) => ({ rows: toModelSpendRows(rows) }),\n );\n}\n\n/** timeseries → daily spend + tokens trend. */\nexport function agentUsageTrendProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.usage.trend',\n (queries, range) => queries.usageTrend(range),\n (points) => ({ rows: toUsageTrendRows(points) }),\n );\n}\n\n/** table → spend per acting ref (user/tenant). */\nexport function agentActorSpendTableProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.byActor',\n (queries, range) => queries.spendByActor(range),\n (rows) => ({ rows: toActorSpendRows(rows) }),\n );\n}\n\n/** breakdown → spend share per actor. */\nexport function agentSpendByActorProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.spend.byActorShare',\n (queries, range) => queries.spendByActor(range),\n (rows) => ({ segments: toActorSpendSegments(rows) }),\n );\n}\n\n/** table → top threads by cost (title, actor, requests, tokens, cost). */\nexport function agentTopThreadsTableProvider(): DataProvider {\n return governanceStatProvider(\n 'agent.threads.topSpend',\n (queries, range) => queries.spendByThread(range, TOP_THREADS_LIMIT),\n (rows) => ({ rows: toThreadSpendRows(rows) }),\n );\n}\n","import { subscribe } from 'node:diagnostics_channel';\nimport { channelName } from '@dudousxd/nestjs-diagnostics';\nimport type { Watcher, WatcherContext } from '@dudousxd/nestjs-telescope';\n\nconst AGENT_EVENTS = ['run.started', 'message', 'tool-call', 'quota.exceeded', 'run.finished'];\n\ninterface DiagnosticEnvelope {\n event: string;\n payload: Record<string, unknown>;\n}\n\n/**\n * Records `aviary:agent:*` diagnostics events as Telescope entries of type `agent`. It depends\n * only on the diagnostics channel — not on the agent runtime — so it stays fully decoupled.\n */\nexport class AgentTelescopeWatcher implements Watcher {\n readonly type = 'agent';\n\n register(ctx: WatcherContext): void {\n for (const event of AGENT_EVENTS) {\n subscribe(channelName('agent', event), (message) => {\n const envelope = message as DiagnosticEnvelope;\n ctx.record({\n type: 'agent',\n content: { event: envelope.event, ...envelope.payload },\n tags: [envelope.event],\n });\n });\n }\n }\n}\n"],"mappings":"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","names":["defineTelescopeExtension","agentDashboard","id","label","panels","sections","title","cols","kind","data","provider","format","style","series","columns","key","TELESCOPE_STORAGE","fetchEntries","ctx","storage","moduleRef","get","TELESCOPE_STORAGE","strict","page","type","limit","data","ofEvent","entries","event","filter","entry","content","agentRunsProvider","name","resolve","_query","finished","value","length","agentTokensProvider","reduce","sum","inputTokens","outputTokens","agentToolsProvider","calls","slice","reverse","map","toolName","toolType","status","runId","rows","agentToolStatusProvider","counts","Map","call","set","segments","label","AGENT_GOVERNANCE_QUERIES","DEFAULT_TREND_WINDOW_DAYS","ISO_DAY_PATTERN","TOP_THREADS_LIMIT","isRecord","value","isGovernanceQueries","spendByModel","spendByActor","spendByThread","usageTrend","recentToolCalls","recentThreads","resolveGovernanceQueries","ctx","resolved","moduleRef","get","AGENT_GOVERNANCE_QUERIES","strict","isIsoDay","test","todayUtcDay","Date","toISOString","slice","shiftUtcDay","day","deltaDays","shifted","setUTCDate","getUTCDate","resolveRange","query","toDay","fromDay","roundCents","Math","round","totalCostUsd","rows","reduce","sum","row","costUsd","totalTokens","inputTokens","outputTokens","toModelSpendSegments","filter","map","label","modelId","toModelSpendRows","requests","toActorSpendRows","actorRef","toThreadSpendRows","title","threadId","toActorSpendSegments","toUsageTrendRows","points","point","governanceStatProvider","name","fetch","format","resolve","queries","agentSpendTotalProvider","range","agentTokensTotalProvider","agentSpendByModelProvider","segments","agentModelSpendTableProvider","agentUsageTrendProvider","agentActorSpendTableProvider","agentSpendByActorProvider","agentTopThreadsTableProvider","subscribe","channelName","AGENT_EVENTS","AgentTelescopeWatcher","type","register","ctx","event","subscribe","channelName","message","envelope","record","content","payload","tags","agentTelescopeExtension","defineTelescopeExtension","name","watchers","AgentTelescopeWatcher","entryTypes","id","label","dot","dashboards","agentDashboard","dataProviders","agentRunsProvider","agentTokensProvider","agentToolsProvider","agentToolStatusProvider","agentSpendTotalProvider","agentTokensTotalProvider","agentSpendByModelProvider","agentModelSpendTableProvider","agentUsageTrendProvider","agentActorSpendTableProvider","agentSpendByActorProvider","agentTopThreadsTableProvider"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dudousxd/nestjs-agent-telescope",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "nestjs-agent telescope extension — an 'Agent' dashboard tab",
5
5
  "license": "MIT",
6
6
  "author": "Davide Carvalho",
@@ -25,7 +25,7 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "@dudousxd/nestjs-diagnostics": "0.6.1",
28
- "@dudousxd/nestjs-agent-core": "0.3.0"
28
+ "@dudousxd/nestjs-agent-core": "0.3.2"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "@dudousxd/nestjs-telescope": ">=1.15.0"