@agimon-ai/po-log 0.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/dist/apiContracts.cjs +164 -0
  4. package/dist/apiContracts.cjs.map +1 -0
  5. package/dist/apiContracts.d.mts +53 -0
  6. package/dist/apiContracts.d.mts.map +1 -0
  7. package/dist/apiContracts.mjs +159 -0
  8. package/dist/apiContracts.mjs.map +1 -0
  9. package/dist/extensions/server.cjs +1034 -0
  10. package/dist/extensions/server.cjs.map +1 -0
  11. package/dist/extensions/server.d.mts +5 -0
  12. package/dist/extensions/server.d.mts.map +1 -0
  13. package/dist/extensions/server.mjs +1006 -0
  14. package/dist/extensions/server.mjs.map +1 -0
  15. package/dist/extensions/web.mjs +1542 -0
  16. package/dist/extensions/web.mjs.map +1 -0
  17. package/dist/index-CTZMUfTZ.d.mts +185 -0
  18. package/dist/index-CTZMUfTZ.d.mts.map +1 -0
  19. package/dist/index.cjs +13 -0
  20. package/dist/index.d.mts +36 -0
  21. package/dist/index.d.mts.map +1 -0
  22. package/dist/index.mjs +2 -0
  23. package/dist/metrics-DywCpDJ_.mjs +508 -0
  24. package/dist/metrics-DywCpDJ_.mjs.map +1 -0
  25. package/dist/metrics-jCZe13Xg.cjs +573 -0
  26. package/dist/metrics-jCZe13Xg.cjs.map +1 -0
  27. package/dist/metrics.cjs +13 -0
  28. package/dist/metrics.d.mts +2 -0
  29. package/dist/metrics.mjs +2 -0
  30. package/dist/metricsSource-DXZLawQu.mjs +130 -0
  31. package/dist/metricsSource-DXZLawQu.mjs.map +1 -0
  32. package/dist/metricsSource-DdwQNzoY.cjs +135 -0
  33. package/dist/metricsSource-DdwQNzoY.cjs.map +1 -0
  34. package/dist/metricsSource-cZnErdv4.d.mts +51 -0
  35. package/dist/metricsSource-cZnErdv4.d.mts.map +1 -0
  36. package/dist/metricsSource.cjs +3 -0
  37. package/dist/metricsSource.d.mts +18 -0
  38. package/dist/metricsSource.d.mts.map +1 -0
  39. package/dist/metricsSource.mjs +2 -0
  40. package/llms.txt +7 -0
  41. package/package.json +140 -0
  42. package/src/prompts/po-debug-agent/SKILL.md +28 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.mjs","names":["routes","settingMetrics","toolDiagnoseAgent"],"sources":["../../src/types/webMetrics.ts","../../src/types/apiRoutes.ts","../../generated/client.ts","../../src/extensions/(frontend)/setting/_lib/metricsApi.ts","../../src/extensions/(frontend)/setting/_lib/chartScale.ts","../../src/extensions/(frontend)/setting/_components/charts/GroupBars.tsx","../../src/extensions/(frontend)/setting/_components/charts/TimelineChart.tsx","../../src/types/issueGrouping.ts","../../src/extensions/(frontend)/setting/_components/IssuesDetail.tsx","../../src/extensions/(frontend)/setting/_components/IssuesSection.tsx","../../src/extensions/(frontend)/setting/_components/MetricsSummary.tsx","../../src/extensions/(frontend)/setting/_components/ToolMetrics.tsx","../../src/extensions/(frontend)/setting/_components/MetricsReportView.tsx","../../src/extensions/(frontend)/setting/_components/MetricsNotice.tsx","../../src/extensions/(frontend)/setting/_components/MetricsPanel.tsx","../../src/extensions/(frontend)/setting/metrics.panel.web.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/AgentDiagnosticCard.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/diagnose-agent.web.tsx","../../generated/web.ts"],"sourcesContent":["/**\n * The metrics API, shared by this package's hub-scoped route and its cockpit\n * plugin. The two halves run in different processes, so the wire vocabulary is\n * declared here: the `(frontend)` half may reach `src/types` and nothing else\n * on the node side.\n *\n * The shapes are narrower than log-sink-mcp's own report, which carries filter\n * echoes, span counts and token distributions the page never draws.\n * Re-exporting the sink's types would also tie the browser bundle to a node\n * package. What crosses the wire is what a chart reads.\n *\n * Two things the sink does not have, so this contract does not pretend to:\n * money, and per-tool failures. This metrics report records tokens, and its tool rows\n * carry an invocation count with no error field.\n */\n\n/**\n * The segment this package's API is mounted under.\n *\n * Read by the hub's `DoomApi`, which still declares its own base path. The\n * browser does not read it: the generated client is handed the same segment by\n * the build, off the `api/log/` folder that creates the mount, so no URL is\n * spelled twice.\n */\nexport const LOG_API_BASE_PATH = 'log';\n\n/**\n * The dimensions the page offers.\n *\n * log-sink-mcp also groups by 'workflow-run', 'workflow-name', 'job' and\n * 'step'. Those are omitted on purpose: no Po package emits the matching\n * attributes today, so offering them would draw an empty chart and read as a\n * bug rather than as a gap.\n */\nexport const METRICS_DIMENSIONS = ['session', 'agent', 'model', 'provider'] as const;\nexport type MetricsDimension = (typeof METRICS_DIMENSIONS)[number];\n\nexport const METRICS_PERIODS = ['day', 'week', 'month', 'all'] as const;\nexport type MetricsPeriod = (typeof METRICS_PERIODS)[number];\n\nexport const METRICS_QUERY_PARAMS = {\n dimension: 'dimension',\n period: 'period',\n /** Narrows the whole report to one value of the current dimension. */\n focus: 'focus',\n} as const;\n\n/** How many groups and tools one response carries; the page ranks, it does not page. */\nexport const METRICS_GROUP_LIMIT = 20;\n\n/** One point on the token timeline. */\nexport interface MetricsBucket {\n /**\n * The sink's own local-time label ('YYYY-MM-DD', or 'YYYY-MM-DD HH:00' for an\n * hour bucket). Carried rather than derived: bucket boundaries are aligned to\n * local time, so slicing the ISO instant renders the wrong day east of\n * Greenwich.\n */\n label: string;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n}\n\n/** One row of the selected dimension. */\nexport interface MetricsGroup {\n /**\n * The group's identity as the sink reports it. For the session dimension\n * this is an opaque hash, never a session the cockpit can open, because\n * po-telemetry hashes identifier-shaped attributes before export.\n */\n key: string;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n /** Problems the sink detected for this group; 0 when it saw none. */\n issueCount: number;\n failed: boolean;\n}\n\n/**\n * One tool's activity.\n *\n * `calls` counts token-attributed samples, not every execution. External MCP\n * calls without model usage remain queryable as logs but have no sample here.\n * `p90TotalTokens` attributes the whole turn's tokens to each tool in that turn,\n * so it ranks tools rather than measuring their own consumption.\n *\n * There is no failure count here because the sink's tool rows do not carry\n * one. Per-tool failures live in its separate agent-issues report, which the\n * running daemon does not expose over HTTP.\n */\nexport interface MetricsTool {\n name: string;\n calls: number;\n p90TotalTokens: number;\n}\n\n/** Which transport answered, so the page can say where its numbers came from. */\nexport type MetricsTransport = 'http' | 'worker';\n\nexport interface MetricsTotals {\n /** Optional so an older hub remains distinguishable from a measured zero. */\n usageEventCount?: number;\n totalRecords?: number;\n /**\n * The providers' own reported total. It is not inputTokens + outputTokens:\n * providers report these counters independently. Their relationship cannot\n * be inferred from a difference, so the page must not stack them as parts\n * of the total or label the difference as cache traffic.\n */\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n /** Independently reported cache reads, not an inferred remainder. */\n cachedTokens: number;\n reasoningTokens: number;\n groupCount: number;\n /** Groups the sink flagged as having failed, out of groupCount. */\n failedGroups: number;\n issueCount: number;\n}\n\nexport interface MetricsReport {\n /** Echoed query window, so issue details can use the same recorded period. */\n startTime?: string;\n endTime?: string;\n generatedAt: string;\n dimension: MetricsDimension;\n period: MetricsPeriod;\n /**\n * The width of one timeline bucket, chosen by the sink from the period. A\n * long period over a short history collapses to a single bucket, so the page\n * says which unit it is drawing rather than implying a series it does not\n * have.\n */\n bucketUnit: string;\n /**\n * The dimension value this report is narrowed to, echoed back from the sink\n * rather than from the request. A daemon older than the filter ignores it\n * and returns everything, so trusting the request would label unfiltered\n * numbers as filtered; absence here means the drill-down did not happen.\n */\n focus?: string;\n /** Undefined when the source could not say which transport answered. */\n transport?: MetricsTransport;\n totals: MetricsTotals;\n timeline: MetricsBucket[];\n groups: MetricsGroup[];\n tools: MetricsTool[];\n}\n\n/**\n * Why the page has nothing to draw.\n *\n * The sink is a separate daemon that may be missing or simply empty, and those\n * are different things to tell a reader. Neither is an error: the metrics\n * source already treats a rejecting daemon as unavailable rather than fatal.\n *\n * 'no-api' is the third case and belongs to the cockpit rather than the sink.\n * A hub serving a bundle without package APIs mounted answers this route with\n * the SPA shell, and a 200 of HTML must read as a feature that is not\n * installed, not as a corrupt response.\n */\nexport type MetricsUnavailableReason = 'no-sink' | 'no-data' | 'no-api' | 'query-error';\n\nexport interface MetricsUnavailable {\n unavailable: MetricsUnavailableReason;\n detail: string;\n}\n\nexport type MetricsResponse = MetricsReport | MetricsUnavailable;\n\nexport function isMetricsUnavailable(response: MetricsResponse): response is MetricsUnavailable {\n return 'unavailable' in response;\n}\n\nexport function isMetricsDimension(value: string): value is MetricsDimension {\n return (METRICS_DIMENSIONS as readonly string[]).includes(value);\n}\n\nexport function isMetricsPeriod(value: string): value is MetricsPeriod {\n return (METRICS_PERIODS as readonly string[]).includes(value);\n}\n\n/** One tool's failure count, paired with its call count from the metrics report. */\nexport interface IssueToolRow {\n name: string;\n failures: number;\n}\n\n/**\n * One recurring problem, as the sink fingerprints it.\n *\n * `occurrenceCount` is how many times it happened, which is the number a\n * reader acts on. `detail` is the actionable line; `message` is often only a\n * record name such as 'pi.tool_result'.\n */\nexport interface IssueSample {\n fingerprint: string;\n occurrenceCount: number;\n category: string;\n timestamp: string;\n level: string;\n message: string;\n detail: string;\n tool: string | null;\n errorType: string | null;\n agentName: string | null;\n model: string | null;\n statusCode: string | null;\n}\n\n/**\n * The detail behind the issue count.\n *\n * Fetched separately from the report because the running sink exposes no\n * issues route over HTTP, so this costs a subprocess. The page asks for it\n * when a reader opens the section, not with every refresh.\n */\nexport interface IssuesView {\n totalIssues: number;\n uniqueIncidents: number;\n byCategory: Record<string, number>;\n byTool: Record<string, number>;\n byErrorType: Record<string, number>;\n samples: IssueSample[];\n}\n\nexport type IssuesResponse = IssuesView | MetricsUnavailable;\n\nexport function isIssuesUnavailable(response: IssuesResponse): response is MetricsUnavailable {\n return 'unavailable' in response;\n}\n\n/** How many incidents the issues section ranks. */\nexport const ISSUE_SAMPLE_LIMIT = 50;\n\n/** How many tool rows the report ranks, so a failing tool has a denominator. */\nexport const METRICS_TOOL_LIMIT = 15;\n","import { apiResponse, defineApiRoutes } from '@agimon-ai/po-core/web';\n\nimport { METRICS_QUERY_PARAMS, type IssuesResponse, type MetricsResponse } from './webMetrics';\n\n/**\n * This package's routes, as data.\n *\n * No scope and no base path. The build reads both off\n * `src/extensions/(backend)/api/log/`, so the mount is stated once, by the\n * folder that creates it. They used to be written here, in the contract, and\n * again inside every URL the page built, and the copies were free to disagree.\n *\n * This is the one file the routes, the Hono app and the browser all read, so a\n * path cannot move on one side alone.\n */\nexport default defineApiRoutes({\n metrics: {\n method: 'GET',\n path: '/metrics',\n query: [METRICS_QUERY_PARAMS.dimension, METRICS_QUERY_PARAMS.period, METRICS_QUERY_PARAMS.focus],\n response: apiResponse<MetricsResponse>(),\n },\n /**\n * Separate from the report because the hub answers it from a subprocess: the\n * running daemon exposes no issues route, so folding it in would make every\n * refresh wait on the slowest transport.\n */\n issues: {\n method: 'GET',\n path: '/issues',\n query: [METRICS_QUERY_PARAMS.focus],\n response: apiResponse<IssuesResponse>(),\n },\n});\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { createApiClient, sessionApiAddress } from '@agimon-ai/po-core/web';\nimport { sealedTransport } from '@agimon-ai/po-web-security/browser';\n\nimport routes from '../src/types/apiRoutes';\n\n/** Scopes the tree mounts 'log' at. */\nexport const apiScopes = ['global', 'workspace', 'session'] as const;\n\nexport const api = createApiClient(routes, {\n scopes: apiScopes,\n basePath: 'log',\n transport: (input, init) => sealedTransport.fetch(input, init),\n sessionAddress: sessionApiAddress,\n});\n","import type { ApiResult } from '@agimon-ai/po-core/web';\n\nimport { api } from '../../../../../generated/client';\nimport {\n isMetricsUnavailable,\n METRICS_QUERY_PARAMS,\n type MetricsDimension,\n type MetricsPeriod,\n type MetricsReport,\n type MetricsResponse,\n type IssuesResponse,\n} from '../../../../types/webMetrics';\n\n/**\n * The page's half of this package's metrics API. The only place the cockpit\n * talks to the hub for metrics, so if the transport ever changes, it changes\n * here alone.\n *\n * These are adapters now, not a transport. The generated client owns the URL\n * and the sealed transport with it, so nothing here spells a route or reaches\n * for `fetch`; a plugin calling `fetch` directly would send plaintext to the\n * tunnel's relay. What stays is this package's own vocabulary: which of the\n * empty states a reader is shown, and which answers are states rather than\n * failures.\n */\n\nconst UNREACHABLE = 'The cockpit hub is unreachable.';\n\n/**\n * A hub with no package APIs mounted serves the SPA shell for this route, so\n * the answer is a 200 of HTML rather than an error status. The client parses no\n * JSON out of it and reports no body at all, which is read here as an\n * uninstalled feature, because that is what it is.\n */\nconst NO_API_DETAIL = 'This cockpit is running a bundle without the log package API, so there are no metrics to read.';\n\nconst NO_API: MetricsResponse = { unavailable: 'no-api', detail: NO_API_DETAIL };\n\nexport type MetricsResult = { report: MetricsResponse } | { error: string };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** A body the hub sent, or which of the two silences it was. */\ntype Outcome = { body: Record<string, unknown> } | { noApi: true } | { error: string };\n\n/**\n * What the hub's answer means, before either route reads its own shape out of\n * it.\n *\n * `status: 0` is the transport never answering, which covers both a dead hub\n * and a caller that aborted. The client cannot tell those apart, so the signal\n * is asked: an abort is the caller replacing this request, and reporting it\n * would overwrite whatever the replacement renders.\n */\nfunction outcomeOf(result: ApiResult<unknown>, signal?: AbortSignal): Outcome {\n if (result.status === 0) return { error: signal?.aborted === true ? '' : UNREACHABLE };\n const status = `The hub answered ${String(result.status)}.`;\n // No body at all: unparseable and successful is the SPA fallback answering,\n // so the route is not mounted. Unparseable and failed is a hub that broke\n // mid-response, which is only worth the status it carried.\n if (result.data === undefined) return result.ok ? { noApi: true } : { error: status };\n if (!isRecord(result.data)) return { error: 'The hub returned a response the cockpit could not read.' };\n if (!result.ok) return { error: result.error === '' ? status : result.error };\n return { body: result.data };\n}\n\n/**\n * Reads one report.\n *\n * A missing sink is not an error here: the route answers it as an\n * `unavailable` body, so the page can name which of the empty states it is in\n * rather than showing one generic failure.\n */\nexport async function fetchMetrics(\n dimension: MetricsDimension,\n period: MetricsPeriod,\n focus?: string,\n signal?: AbortSignal,\n): Promise<MetricsResult> {\n const result = await api.global.metrics({\n query: {\n [METRICS_QUERY_PARAMS.dimension]: dimension,\n [METRICS_QUERY_PARAMS.period]: period,\n [METRICS_QUERY_PARAMS.focus]: focus === '' ? undefined : focus,\n },\n ...(signal === undefined ? {} : { signal }),\n });\n\n const outcome = outcomeOf(result, signal);\n if ('error' in outcome) return { error: outcome.error };\n if ('noApi' in outcome) return { report: NO_API };\n\n const report = outcome.body as unknown as MetricsResponse;\n if (!isMetricsUnavailable(report) && !Array.isArray(report.groups)) {\n return { error: 'The hub returned a report the cockpit could not read.' };\n }\n return { report };\n}\n\nexport type IssuesResult = { issues: IssuesResponse } | { error: string };\n\n/**\n * Reads the detail behind the issue count.\n *\n * Separate call because the hub answers it from a subprocess: folding it into\n * the report would make every refresh wait on the slowest transport.\n */\nexport async function fetchIssues(\n focus?: string,\n signal?: AbortSignal,\n window: Pick<MetricsReport, 'startTime' | 'endTime'> = {},\n): Promise<IssuesResult> {\n const result = await api.global.issues({\n query: { [METRICS_QUERY_PARAMS.focus]: focus === '' ? undefined : focus, ...window },\n ...(signal === undefined ? {} : { signal }),\n });\n\n const outcome = outcomeOf(result, signal);\n if ('error' in outcome) return { error: outcome.error };\n if ('noApi' in outcome) return { issues: { unavailable: 'no-api', detail: NO_API_DETAIL } };\n return { issues: outcome.body as unknown as IssuesResponse };\n}\n","/**\n * The arithmetic behind the charts, kept out of the components so it can be\n * tested as plain functions rather than through a render.\n *\n * There is no charting library in the cockpit bundle and the plugin import\n * allowlist would not admit one, so these are the primitives the SVG is drawn\n * from. They are deliberately small: a bar length and a tick set, nothing that\n * pretends to be a plotting engine.\n */\n\n/** Bar length as a fraction of the track, guarding the all-zero series. */\nexport function barFraction(value: number, max: number): number {\n if (!Number.isFinite(value) || value <= 0) return 0;\n if (!Number.isFinite(max) || max <= 0) return 0;\n return Math.min(1, value / max);\n}\n\n/** The largest value in a series, or 0 for an empty one. */\nexport function seriesMax(values: readonly number[]): number {\n return values.reduce((highest, value) => (Number.isFinite(value) && value > highest ? value : highest), 0);\n}\n\n/**\n * Compact token counts. Charts label axes and bars in a few characters, and a\n * raw nine-digit total makes every row the same illegible width.\n */\nexport function formatTokens(value: number): string {\n // A field the hub did not send is unknown, not zero. Rendering it as '0'\n // states a fact nobody measured, which is how a version skew between the\n // page bundle and the hub API turns into a confident wrong number.\n if (!Number.isFinite(value)) return 'Not reported';\n if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`;\n if (value >= 1_000) return `${(value / 1_000).toFixed(1)}k`;\n return String(Math.round(value));\n}\n\n/**\n * Evenly spaced x positions for a series, in the 0..1 range.\n *\n * A single point sits at the left edge rather than dividing by zero, which is\n * what a one-bucket timeline is: the sink answered, there is just one day.\n */\nexport function evenPositions(count: number): number[] {\n if (count <= 0) return [];\n if (count === 1) return [0];\n return Array.from({ length: count }, (_, index) => index / (count - 1));\n}\n","import { Button } from '@agimon-ai/po-web-components';\nimport { useState } from 'react';\n\nimport type { MetricsGroup } from '../../../../../types/webMetrics';\nimport { barFraction, formatTokens, seriesMax } from '../../_lib/chartScale';\n\nconst PREVIEW_GROUPS = 5;\n\ninterface GroupBarsProps {\n groups: readonly MetricsGroup[];\n totalTokens?: number;\n focus?: string;\n onFocus?: (key: string) => void;\n}\n\n/** Rank recorded consumers; an unknown attribution is not a selectable model. */\nexport function GroupBars({ groups, totalTokens, focus, onFocus }: GroupBarsProps) {\n const [expanded, setExpanded] = useState(false);\n const sorted = [...groups].sort((a, b) => b.totalTokens - a.totalTokens || a.key.localeCompare(b.key));\n const rows = expanded ? sorted : sorted.slice(0, PREVIEW_GROUPS);\n const max = totalTokens ?? seriesMax(groups.map((group) => group.totalTokens));\n return (\n <div className=\"flex flex-col gap-3\">\n {groups.length === 0 ? (\n <p className=\"rounded-md border border-dashed border-doom-border p-5 text-sm text-doom-dim\">\n No grouped usage was recorded for this selection.\n </p>\n ) : null}\n <ul className=\"flex flex-col gap-1\" data-testid=\"metrics-group-bars\">\n {rows.map((group) => {\n const selected = focus === group.key;\n const unknown = group.key === 'unknown' || group.key === '';\n const canFocus = onFocus !== undefined && !unknown;\n const percentage = barFraction(group.totalTokens, max) * 100;\n const row = (\n <>\n <span className=\"flex items-start justify-between gap-3\">\n <span className=\"min-w-0 break-all text-left font-medium text-doom-hi\" title={group.key}>\n {unknown ? 'Unattributed' : group.key}\n </span>\n <span\n className=\"shrink-0 text-right font-semibold tabular-nums text-doom-hi\"\n title={String(group.totalTokens)}\n >\n {formatTokens(group.totalTokens)}\n </span>\n </span>\n <span aria-hidden=\"true\" className=\"mt-2 block h-1.5 overflow-hidden rounded-full bg-doom-border\">\n <span\n className={`block h-full rounded-full ${unknown ? 'bg-doom-yellow' : 'bg-doom-blue'}`}\n style={{ width: `${percentage}%` }}\n />\n </span>\n <span className=\"mt-2 flex flex-wrap items-center justify-between gap-2 text-sm text-doom-dim\">\n <span>\n {totalTokens !== undefined && max > 0\n ? `${percentage.toFixed(1)}% of total`\n : 'Relative token volume'}\n </span>\n <span className={group.issueCount > 0 || group.failed ? 'text-doom-yellow' : 'text-doom-dim'}>\n {group.failed ? 'Failed · ' : ''}\n {formatTokens(group.issueCount)} {group.issueCount === 1 ? 'issue' : 'issues'}\n </span>\n </span>\n </>\n );\n return (\n <li key={group.key}>\n {canFocus ? (\n <button\n type=\"button\"\n onClick={() => onFocus?.(selected ? '' : group.key)}\n aria-pressed={selected}\n aria-label={`Filter to ${group.key}`}\n data-testid={`metrics-group-${group.key}`}\n className={`block w-full rounded-md p-3 text-base hover:bg-doom-tint-blue focus-visible:outline focus-visible:outline-1 focus-visible:outline-doom-blue ${selected ? 'bg-doom-tint-blue' : ''}`}\n >\n {row}\n </button>\n ) : (\n <div className=\"rounded-md p-3 text-base\">{row}</div>\n )}\n </li>\n );\n })}\n </ul>\n {groups.length > PREVIEW_GROUPS ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setExpanded(!expanded)} aria-expanded={expanded}>\n {expanded ? 'Show top 5' : `Show all ${groups.length} returned groups`}\n </Button>\n ) : null}\n </div>\n );\n}\n","import { Button } from '@agimon-ai/po-web-components';\nimport { useState } from 'react';\n\nimport type { MetricsBucket } from '../../../../../types/webMetrics';\nimport { barFraction, formatTokens, seriesMax } from '../../_lib/chartScale';\n\nconst SERIES = [\n { key: 'totalTokens', label: 'Total' },\n { key: 'inputTokens', label: 'Input' },\n { key: 'outputTokens', label: 'Output' },\n] as const;\nconst PLOT_WIDTH = 600;\nconst PLOT_HEIGHT = 160;\nconst MAX_COLUMN_WIDTH = 48;\n\ninterface TimelineChartProps {\n buckets: readonly MetricsBucket[];\n bucketUnit?: string;\n onShorterPeriod?: () => void;\n}\n\n/** Plot the sink's buckets without filling missing history or stacking counters. */\nexport function TimelineChart({ buckets, bucketUnit, onShorterPeriod }: TimelineChartProps) {\n const [series, setSeries] = useState<(typeof SERIES)[number]['key']>('totalTokens');\n const [activeLabel, setActiveLabel] = useState('');\n const active = buckets.find((bucket) => bucket.label === activeLabel) ?? buckets.at(-1);\n const max = seriesMax(buckets.map((bucket) => bucket[series]));\n const unit = bucketUnit ? `${bucketUnit} ` : '';\n const slot = PLOT_WIDTH / Math.max(1, buckets.length);\n const width = Math.min(MAX_COLUMN_WIDTH, slot * 0.65);\n\n if (buckets.length === 0) {\n return (\n <p\n className=\"rounded-md border border-dashed border-doom-border p-5 text-sm text-doom-dim\"\n data-testid=\"metrics-timeline-chart\"\n >\n No timeline buckets were recorded for this selection.\n </p>\n );\n }\n\n if (buckets.length === 1 && active !== undefined) {\n return (\n <div className=\"flex flex-col gap-4\" data-testid=\"metrics-timeline-chart\">\n <div className=\"rounded-md border border-doom-border bg-doom-deep p-5\">\n <p className=\"text-sm text-doom-dim\">{active.label}</p>\n <p className=\"mt-2 text-lg font-semibold tabular-nums text-doom-blue\" title={String(active.totalTokens)}>\n {formatTokens(active.totalTokens)} tokens\n </p>\n <p className=\"mt-3 text-sm leading-relaxed text-doom-dim\">\n Only one {unit}bucket was recorded. There is not enough history to show a trend.\n </p>\n <p className=\"mt-2 text-sm text-doom-dim\">For more detail, pick a shorter period when one is available.</p>\n {onShorterPeriod === undefined ? null : (\n <Button variant=\"ghost\" size=\"sm\" className=\"mt-3\" onClick={onShorterPeriod}>\n View hourly detail\n </Button>\n )}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col gap-3\" data-testid=\"metrics-timeline-chart\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex gap-1\" role=\"group\" aria-label=\"Timeline token counter\">\n {SERIES.map((item) => (\n <Button\n key={item.key}\n size=\"sm\"\n variant={series === item.key ? 'primary' : 'ghost'}\n aria-pressed={series === item.key}\n onClick={() => setSeries(item.key)}\n >\n {item.label}\n </Button>\n ))}\n </div>\n <span className=\"text-sm text-doom-dim\">\n {buckets.length} recorded {unit}buckets\n </span>\n </div>\n <div className=\"flex gap-3\">\n <div\n aria-hidden=\"true\"\n className=\"flex w-12 shrink-0 flex-col justify-between text-right text-sm tabular-nums text-doom-dim\"\n >\n <span>{formatTokens(max)}</span>\n <span>{formatTokens(max / 2)}</span>\n <span>0</span>\n </div>\n <svg\n viewBox={`0 0 ${PLOT_WIDTH} ${PLOT_HEIGHT}`}\n preserveAspectRatio=\"none\"\n className=\"h-40 min-w-0 flex-1 overflow-visible\"\n role=\"group\"\n aria-label={`tokens per bucket, peak ${formatTokens(max)}`}\n >\n {[0, PLOT_HEIGHT / 2, PLOT_HEIGHT].map((y) => (\n <line\n key={y}\n x1=\"0\"\n x2={PLOT_WIDTH}\n y1={y}\n y2={y}\n className=\"text-doom-border\"\n stroke=\"currentColor\"\n vectorEffect=\"non-scaling-stroke\"\n />\n ))}\n {buckets.map((bucket, index) => {\n const height = barFraction(bucket[series], max) * PLOT_HEIGHT;\n const label = `${bucket.label}: ${formatTokens(bucket[series])} ${SERIES.find((item) => item.key === series)?.label.toLowerCase()} tokens`;\n return (\n <g key={bucket.label}>\n <rect\n x={index * slot + (slot - width) / 2}\n y={PLOT_HEIGHT - height}\n width={width}\n height={height}\n rx=\"2\"\n fill=\"currentColor\"\n className={active?.label === bucket.label ? 'text-doom-cyan' : 'text-doom-blue'}\n />\n <rect\n x={index * slot}\n y=\"0\"\n width={slot}\n height={PLOT_HEIGHT}\n fill=\"transparent\"\n tabIndex={0}\n role=\"button\"\n aria-label={label}\n aria-pressed={active?.label === bucket.label}\n onMouseEnter={() => setActiveLabel(bucket.label)}\n onFocus={() => setActiveLabel(bucket.label)}\n onClick={() => setActiveLabel(bucket.label)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n setActiveLabel(bucket.label);\n }\n }}\n className=\"cursor-pointer focus-visible:outline focus-visible:outline-1 focus-visible:outline-doom-cyan\"\n >\n <title>{label}</title>\n </rect>\n </g>\n );\n })}\n </svg>\n </div>\n <div className=\"ml-15 flex justify-between gap-3 text-sm text-doom-dim\">\n <span>{buckets[0]?.label}</span>\n <span className=\"text-right\">{buckets.at(-1)?.label}</span>\n </div>\n <div\n className=\"flex flex-wrap justify-between gap-2 rounded-md bg-doom-deep px-3 py-2 text-sm tabular-nums\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n <span className=\"text-doom-dim\">{active?.label}</span>\n <span className=\"text-doom-hi\">\n {formatTokens(active?.[series] ?? Number.NaN)}{' '}\n {SERIES.find((item) => item.key === series)?.label.toLowerCase()} tokens\n </span>\n </div>\n {max === 0 ? (\n <p className=\"text-sm text-doom-dim\">\n Recorded buckets contain zero {SERIES.find((item) => item.key === series)?.label.toLowerCase()} tokens.\n </p>\n ) : null}\n <details className=\"text-sm text-doom-dim\">\n <summary className=\"cursor-pointer rounded-sm focus-visible:outline focus-visible:outline-1 focus-visible:outline-doom-blue\">\n View exact bucket values\n </summary>\n <div className=\"mt-3 overflow-x-auto\">\n <table className=\"w-full text-right tabular-nums\">\n <caption className=\"sr-only\">Independent token counters by recorded bucket</caption>\n <thead>\n <tr>\n <th scope=\"col\" className=\"py-2 text-left\">\n Bucket\n </th>\n <th scope=\"col\" className=\"pl-3\">\n Total\n </th>\n <th scope=\"col\" className=\"pl-3\">\n Input\n </th>\n <th scope=\"col\" className=\"pl-3\">\n Output\n </th>\n </tr>\n </thead>\n <tbody>\n {buckets.map((bucket) => (\n <tr key={bucket.label} className=\"border-t border-doom-border\">\n <th scope=\"row\" className=\"py-2 text-left font-normal\">\n {bucket.label}\n </th>\n {SERIES.map((item) => (\n <td key={item.key} className=\"pl-3\">\n {Number.isFinite(bucket[item.key]) ? bucket[item.key].toLocaleString('en-US') : 'Not reported'}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </details>\n </div>\n );\n}\n","import type { IssueSample } from './webMetrics';\n\n/**\n * Turning a list of incidents into a ranked list of problems.\n *\n * Pure, and in types/ so both halves may use it: the page ranks, and a test\n * can assert the ranking without a render or a subprocess.\n *\n * Two things the sink's own output needs fixing for. It can emit the same\n * fingerprint more than once, so occurrences have to be re-summed or the same\n * problem appears three times at a third of its real weight. And its ordering\n * is by recency, which buries a problem that happened twenty times under one\n * that happened once a minute ago.\n */\n\nexport interface IssueGroup {\n /** Stable identity for a row, unique after merging. */\n key: string;\n /** How many times this exact problem happened. */\n occurrences: number;\n category: string;\n /** The actionable line: the spawn that failed, the path that was missing. */\n detail: string;\n tool: string | null;\n errorType: string | null;\n agentName: string | null;\n model: string | null;\n statusCode: string | null;\n /** Most recent occurrence across the merged incidents. */\n lastSeen: string;\n /** Every incident folded into this row, for the expanded view. */\n members: IssueSample[];\n}\n\n/**\n * A key that separates problems a reader would act on separately.\n *\n * The sink's fingerprint is the primary key, but it can be empty, and several\n * distinct bash failures share 'tool_failure|pi|bash||pi.tool_result' because\n * the record name is all they carry. Folding the detail in keeps two different\n * failures apart while still merging repeats of the same one.\n */\nexport function groupingKey(sample: IssueSample): string {\n const base = sample.fingerprint === '' ? `${sample.category}|${sample.message}` : sample.fingerprint;\n return `${base}|${sample.detail}`;\n}\n\n/** Incidents merged by problem and ranked by how often each happened. */\nexport function groupIssues(samples: readonly IssueSample[]): IssueGroup[] {\n const groups = new Map<string, IssueGroup>();\n\n for (const sample of samples) {\n const key = groupingKey(sample);\n const existing = groups.get(key);\n if (existing === undefined) {\n groups.set(key, {\n key,\n occurrences: sample.occurrenceCount,\n category: sample.category,\n detail: sample.detail,\n tool: sample.tool,\n errorType: sample.errorType,\n agentName: sample.agentName,\n model: sample.model,\n statusCode: sample.statusCode,\n lastSeen: sample.timestamp,\n members: [sample],\n });\n continue;\n }\n existing.occurrences += sample.occurrenceCount;\n existing.members.push(sample);\n if (sample.timestamp > existing.lastSeen) existing.lastSeen = sample.timestamp;\n // A merged row keeps whichever identifying field any member supplied, so\n // one incident missing an agent name does not blank it for the group.\n existing.tool ??= sample.tool;\n existing.errorType ??= sample.errorType;\n existing.agentName ??= sample.agentName;\n existing.model ??= sample.model;\n existing.statusCode ??= sample.statusCode;\n }\n\n return [...groups.values()].sort(\n (left, right) => right.occurrences - left.occurrences || left.detail.localeCompare(right.detail),\n );\n}\n","import { useState } from 'react';\n\nimport { groupIssues, type IssueGroup } from '../../../../types/issueGrouping';\nimport type { IssuesView, MetricsTool } from '../../../../types/webMetrics';\nimport { barFraction } from '../_lib/chartScale';\n\n/**\n * What is actually going wrong, ranked by how often.\n *\n * A count of 69 issues tells nobody what to change. The same 69 collapsed into\n * \"this spawn failed 20 times, this hook failed 3\" is a work list, so the\n * ranked bar is the primary view here and the totals are context beside it.\n *\n * Each bar expands to the incidents behind it, because the row states the\n * problem and the reader still needs the session, model, and timestamps to go\n * and look at one.\n */\n\nfunction countRows(counts: Record<string, number>): [string, number][] {\n return Object.entries(counts).sort(([, left], [, right]) => right - left);\n}\n\n/** A short, human label for a problem; the detail can be a whole command line. */\nfunction titleOf(group: IssueGroup): string {\n if (group.errorType !== null) return group.errorType;\n if (group.tool !== null) return `${group.tool} failed`;\n return group.category;\n}\n\ninterface IssueRowProps {\n group: IssueGroup;\n max: number;\n tools: readonly MetricsTool[];\n}\n\nfunction IssueRow({ group, max, tools }: IssueRowProps) {\n const [open, setOpen] = useState(false);\n const width = `${(barFraction(group.occurrences, max) * 100).toFixed(2)}%`;\n const calls = tools.find((tool) => tool.name === group.tool)?.calls;\n\n return (\n <li className=\"flex flex-col rounded-md border border-doom-border\">\n <div className=\"relative\">\n <span\n aria-hidden=\"true\"\n className=\"absolute bottom-0 left-0 h-1 rounded-xs bg-doom-yellow/40\"\n style={{ width }}\n />\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n aria-expanded={open}\n data-testid={`metrics-issue-${group.key}`}\n className=\"relative flex min-h-10 w-full flex-wrap items-start gap-3 rounded-md p-3 text-left text-base hover:bg-doom-tint-blue focus-visible:outline focus-visible:outline-1 focus-visible:outline-doom-blue\"\n >\n <span className=\"w-8 shrink-0 text-right font-semibold text-doom-yellow\">{group.occurrences}</span>\n <span className=\"w-24 shrink-0 break-words font-medium text-doom-hi\">{titleOf(group)}</span>\n <span className=\"min-w-40 flex-1 break-words text-doom-dim\">{group.detail}</span>\n {calls === undefined ? null : <span className=\"shrink-0 text-doom-dim\">{calls} token samples</span>}\n </button>\n </div>\n\n {open ? (\n <div className=\"flex flex-col gap-1 px-3 py-3 text-base\" data-testid={`metrics-issue-body-${group.key}`}>\n <div className=\"flex flex-wrap gap-x-3 gap-y-1 text-sm text-doom-dim\">\n <span>category {group.category}</span>\n {group.tool === null ? null : <span>tool {group.tool}</span>}\n {group.errorType === null ? null : <span>error {group.errorType}</span>}\n {group.statusCode === null ? null : <span>status {group.statusCode}</span>}\n {group.agentName === null ? null : <span>agent {group.agentName}</span>}\n {group.model === null ? null : <span>model {group.model}</span>}\n <span>last seen {group.lastSeen}</span>\n </div>\n <span className=\"break-words text-doom-dim\">{group.detail}</span>\n <ul className=\"flex flex-col gap-1 text-sm text-doom-dim\">\n {group.members.map((member, index) => (\n <li key={`${member.timestamp}-${String(index)}`} className=\"flex flex-wrap gap-x-2\">\n <span>{member.timestamp}</span>\n <span>{member.level}</span>\n <span>\n {member.occurrenceCount}\n {'\\u00d7'}\n </span>\n <span className=\"min-w-0 truncate\">{member.message}</span>\n </li>\n ))}\n </ul>\n </div>\n ) : null}\n </li>\n );\n}\n\nexport interface IssuesDetailProps {\n /** Comparable metrics count, omitted when issue analysis has a broader filter. */\n summaryCount?: number;\n view: IssuesView;\n /** Token-attributed tool calls provide context, not a failure-rate denominator. */\n tools: readonly MetricsTool[];\n}\n\nexport function IssuesDetail({ view, tools, summaryCount }: IssuesDetailProps) {\n const groups = groupIssues(view.samples);\n const max = groups[0]?.occurrences ?? 0;\n const callsByTool = new Map(tools.map((tool) => [tool.name, tool.calls]));\n\n return (\n <div className=\"flex flex-col gap-3\">\n {summaryCount !== undefined && summaryCount !== view.totalIssues ? (\n <p\n className=\"rounded-md border border-doom-edge-yellow bg-doom-tint-yellow p-3 text-sm leading-relaxed text-doom-yellow\"\n role=\"status\"\n data-testid=\"metrics-issue-count-mismatch\"\n >\n The summary reports {summaryCount} {summaryCount === 1 ? 'issue' : 'issues'}; incident analysis returned{' '}\n {view.totalIssues}. The counts differ, so these details should not be treated as a complete explanation of the\n summary.\n </p>\n ) : null}\n <span className=\"text-sm text-doom-dim\">\n <span className=\"text-doom-hi\">{view.totalIssues}</span> occurrences of{' '}\n <span className=\"text-doom-hi\">{groups.length}</span> distinct problems in the returned samples, most frequent\n first\n </span>\n\n {groups.length === 0 ? (\n <p className=\"rounded-md border border-dashed border-doom-border p-4 text-sm text-doom-dim\">\n {view.totalIssues === 0\n ? 'No issues were detected in the returned analysis. This does not establish coverage of unrecorded activity.'\n : 'The analysis counted issues but returned no incident samples.'}\n </p>\n ) : (\n <ul className=\"flex flex-col gap-2\" data-testid=\"metrics-issue-groups\">\n {groups.map((group) => (\n <IssueRow key={group.key} group={group} max={max} tools={tools} />\n ))}\n </ul>\n )}\n\n {Object.keys(view.byTool).length === 0 ? null : (\n <div className=\"flex flex-col gap-2 overflow-x-auto\">\n <h4 className=\"text-base font-semibold text-doom-hi\">Failures by tool</h4>\n <table className=\"w-full text-sm\" data-testid=\"metrics-issues-tools\">\n <thead>\n <tr className=\"text-sm text-doom-dim\">\n <th scope=\"col\" className=\"py-2 text-left font-normal\">\n Tool\n </th>\n <th scope=\"col\" className=\"text-right font-normal\">\n Failures\n </th>\n <th scope=\"col\" className=\"text-right font-normal\">\n Attribution context\n </th>\n </tr>\n </thead>\n <tbody>\n {countRows(view.byTool).map(([name, failures]) => {\n const calls = callsByTool.get(name);\n return (\n <tr key={name} className=\"border-b border-doom-border/40\">\n <td className=\"min-w-0 break-all py-2 text-doom-dim\">{name}</td>\n <td className=\"w-16 py-2 text-right text-doom-red\">{failures}</td>\n <td className=\"w-28 py-2 text-right text-doom-dim\">\n {/* These samples need not cover the calls that failed. */}\n {calls === undefined ? 'no token samples' : `${String(calls)} token samples`}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n )}\n\n <div className=\"flex flex-wrap gap-x-4 gap-y-1 text-sm text-doom-dim\">\n <span className=\"text-sm font-semibold text-doom-dim\">by category</span>\n {countRows(view.byCategory).map(([name, count]) => (\n <span key={name}>\n {name} <span className=\"text-doom-hi\">{count}</span>\n </span>\n ))}\n </div>\n </div>\n );\n}\n","import { Button, Spinner } from '@agimon-ai/po-web-components';\nimport { useEffect, useId, useRef, useState } from 'react';\n\nimport { isIssuesUnavailable, type IssuesView, type MetricsTool } from '../../../../types/webMetrics';\nimport { fetchIssues } from '../_lib/metricsApi';\nimport { IssuesDetail } from './IssuesDetail';\n\ninterface IssuesSectionProps {\n tools: readonly MetricsTool[];\n focus?: string;\n count?: number;\n startTime?: string;\n endTime?: string;\n broaderScope?: boolean;\n}\n\n/** Issue analysis is an explicit read, not another request on every metrics refresh. */\nexport function IssuesSection({ tools, focus, count, startTime, endTime, broaderScope = false }: IssuesSectionProps) {\n const [view, setView] = useState<IssuesView>();\n const [message, setMessage] = useState('');\n const [loading, setLoading] = useState(false);\n const [open, setOpen] = useState(false);\n const pending = useRef<AbortController | undefined>(undefined);\n const detailsId = useId();\n useEffect(() => {\n pending.current?.abort();\n setView(undefined);\n setMessage('');\n setLoading(false);\n setOpen(false);\n return () => pending.current?.abort();\n }, [focus, startTime, endTime]);\n\n const load = (): void => {\n pending.current?.abort();\n const controller = new AbortController();\n pending.current = controller;\n setOpen(true);\n setLoading(true);\n setMessage('');\n setView(undefined);\n void fetchIssues(focus, controller.signal, { startTime, endTime })\n .then((result) => {\n if (controller.signal.aborted) return;\n setLoading(false);\n if ('error' in result) setMessage(result.error || 'Issue details could not be loaded.');\n else if (isIssuesUnavailable(result.issues)) setMessage(result.issues.detail);\n else setView(result.issues);\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) return;\n setLoading(false);\n setMessage(error instanceof Error ? error.message : 'Issue details could not be loaded.');\n });\n };\n return (\n <section\n className=\"flex min-w-0 flex-col gap-3 rounded-lg border border-doom-border bg-doom-panel p-4\"\n data-testid=\"metrics-issues\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h3 className=\"text-lg font-semibold text-doom-hi\">\n Issues to investigate{count === undefined ? '' : ` · ${count} ${count === 1 ? 'issue' : 'issues'}`}\n </h3>\n <p className=\"mt-1 text-sm text-doom-dim\">Inspect recurring failures and the evidence behind them.</p>\n </div>\n <div className=\"flex items-center gap-2\">\n {loading ? <Spinner label=\"Reading issue details\" /> : null}\n <Button\n variant=\"outline\"\n size=\"md\"\n disabled={loading}\n onClick={load}\n aria-expanded={open}\n aria-controls={detailsId}\n data-testid={open ? 'metrics-issues-reload' : 'metrics-issues-open'}\n >\n {open ? 'Reload details' : 'Inspect issues'}\n </Button>\n {open ? (\n <Button\n variant=\"ghost\"\n size=\"md\"\n onClick={() => {\n pending.current?.abort();\n setLoading(false);\n setOpen(false);\n }}\n aria-controls={detailsId}\n >\n Hide\n </Button>\n ) : null}\n </div>\n </div>\n <p className=\"text-sm leading-relaxed text-doom-dim\">\n Details are loaded on demand.{' '}\n {startTime === undefined\n ? endTime === undefined\n ? 'This hub did not report a time window; details cover all available history.'\n : 'Details cover available history up to report time; no start bound was supplied.'\n : 'Details use the report’s recorded time window.'}{' '}\n {broaderScope\n ? 'LogSink cannot filter issue details by model, provider or agent, so this analysis covers all groups in that window.'\n : focus === undefined\n ? 'Analysis covers all groups.'\n : 'Filtered to this session.'}\n </p>\n <div id={detailsId} hidden={!open} aria-busy={loading}>\n {message === '' ? null : (\n <p\n role=\"alert\"\n className=\"rounded-md border border-doom-edge-yellow bg-doom-tint-yellow p-3 text-sm text-doom-yellow\"\n data-testid=\"metrics-issues-message\"\n >\n {message}\n </p>\n )}\n {loading ? (\n <p role=\"status\" className=\"py-3 text-sm text-doom-dim\">\n Reading recorded incidents...\n </p>\n ) : null}\n {view === undefined ? null : (\n <IssuesDetail view={view} tools={broaderScope ? [] : tools} summaryCount={broaderScope ? undefined : count} />\n )}\n </div>\n </section>\n );\n}\n","import type { MetricsTotals } from '../../../../types/webMetrics';\nimport { formatTokens } from '../_lib/chartScale';\n\n/** Independent counters, never a stacked breakdown of the provider total. */\nexport function MetricsSummary({ totals }: { totals: MetricsTotals }) {\n const cards = [\n { label: 'Total tokens', value: totals.totalTokens, detail: 'Provider-reported total', color: 'text-doom-blue' },\n { label: 'Input tokens', value: totals.inputTokens, detail: 'Reported input counter', color: 'text-doom-hi' },\n { label: 'Output tokens', value: totals.outputTokens, detail: 'Reported output counter', color: 'text-doom-hi' },\n {\n label: 'Recorded issues',\n value: totals.issueCount,\n detail: 'Detected in this selection',\n color: totals.issueCount > 0 ? 'text-doom-yellow' : 'text-doom-hi',\n },\n ];\n return (\n <dl className=\"grid grid-cols-2 gap-3 @3xl:grid-cols-4\" data-testid=\"metrics-summary\">\n {cards.map((card) => (\n <div key={card.label} className=\"min-w-0 rounded-lg border border-doom-border bg-doom-panel p-4\">\n <dt className=\"text-sm text-doom-dim\">{card.label}</dt>\n <dd\n className={`mt-2 text-lg font-semibold tabular-nums ${card.color}`}\n title={Number.isFinite(card.value) ? card.value.toLocaleString('en-US') : 'Not reported'}\n >\n {formatTokens(card.value)}\n </dd>\n <dd className=\"mt-1 text-sm text-doom-dim\">{card.detail}</dd>\n </div>\n ))}\n </dl>\n );\n}\n","import { Button } from '@agimon-ai/po-web-components';\nimport { useState } from 'react';\n\nimport type { MetricsTool } from '../../../../types/webMetrics';\nimport { formatTokens } from '../_lib/chartScale';\n\n/** The sink attributes a whole turn to its tools. These are samples, not bills. */\nexport function ToolMetrics({ tools }: { tools: readonly MetricsTool[] }) {\n const [sort, setSort] = useState<'calls' | 'p90TotalTokens'>('p90TotalTokens');\n const rows = [...tools].sort((a, b) => b[sort] - a[sort] || a.name.localeCompare(b.name));\n return (\n <section\n className=\"flex min-w-0 flex-col gap-4 rounded-lg border border-doom-border bg-doom-panel p-4\"\n aria-label=\"Tool token samples\"\n >\n <div>\n <h3 className=\"text-lg font-semibold text-doom-hi\">Tool token samples</h3>\n <p className=\"mt-1 text-sm text-doom-dim\">Which tools participate in token-heavy turns?</p>\n </div>\n {rows.length === 0 ? (\n <div\n className=\"flex flex-1 flex-col justify-center gap-2 rounded-md border border-dashed border-doom-border p-5\"\n data-testid=\"metrics-tools-empty\"\n >\n <p className=\"text-base font-semibold text-doom-hi\">No token-attributed tool calls</p>\n <p className=\"text-sm leading-relaxed text-doom-dim\">\n Tool execution logs may exist, but this selection has no samples that connect tools to model usage. This\n does not mean no tools ran.\n </p>\n </div>\n ) : (\n <>\n <div className=\"flex flex-wrap gap-1\" role=\"group\" aria-label=\"Rank tool samples by\">\n <Button\n size=\"sm\"\n variant={sort === 'p90TotalTokens' ? 'primary' : 'ghost'}\n aria-pressed={sort === 'p90TotalTokens'}\n onClick={() => setSort('p90TotalTokens')}\n >\n P90 turn tokens\n </Button>\n <Button\n size=\"sm\"\n variant={sort === 'calls' ? 'primary' : 'ghost'}\n aria-pressed={sort === 'calls'}\n onClick={() => setSort('calls')}\n >\n Sampled calls\n </Button>\n </div>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-base tabular-nums\" data-testid=\"metrics-tools\">\n <caption className=\"sr-only\">\n Tools ranked by {sort === 'calls' ? 'sampled calls' : '90th percentile whole-turn tokens'}\n </caption>\n <thead>\n <tr className=\"border-b border-doom-border text-sm text-doom-dim\">\n <th scope=\"col\" className=\"pb-2 text-left font-normal\">\n Tool\n </th>\n <th scope=\"col\" className=\"pb-2 pl-3 text-right font-normal\">\n Sampled calls\n </th>\n <th scope=\"col\" className=\"pb-2 pl-3 text-right font-normal\">\n P90 turn tokens\n </th>\n </tr>\n </thead>\n <tbody>\n {rows.map((tool) => (\n <tr key={tool.name} className=\"border-b border-doom-border/40\">\n <th scope=\"row\" className=\"max-w-48 break-all py-3 text-left font-normal text-doom-hi\">\n {tool.name}\n </th>\n <td className=\"py-3 pl-3 text-right text-doom-dim\">{formatTokens(tool.calls)}</td>\n <td className=\"py-3 pl-3 text-right text-doom-blue\" title={String(tool.p90TotalTokens)}>\n {formatTokens(tool.p90TotalTokens)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </>\n )}\n <details className=\"text-sm leading-relaxed text-doom-dim\">\n <summary className=\"cursor-pointer rounded-sm focus-visible:outline focus-visible:outline-1 focus-visible:outline-doom-blue\">\n How these samples are counted\n </summary>\n <p className=\"mt-2\">\n This view includes only calls with recorded token attribution; external MCP calls remain in the logs. P90 is\n the 90th percentile of the whole turn, not each tool&apos;s own consumption. Several tools may share a turn,\n so do not add these values together.\n </p>\n </details>\n </section>\n );\n}\n","import type { MetricsDimension, MetricsPeriod, MetricsReport } from '../../../../types/webMetrics';\nimport { formatTokens } from '../_lib/chartScale';\nimport { GroupBars } from './charts/GroupBars';\nimport { TimelineChart } from './charts/TimelineChart';\nimport { IssuesSection } from './IssuesSection';\nimport { MetricsSummary } from './MetricsSummary';\nimport { ToolMetrics } from './ToolMetrics';\n\nexport const DIMENSION_LABELS: Record<MetricsDimension, string> = {\n session: 'session',\n agent: 'agent',\n model: 'model',\n provider: 'provider',\n};\nexport const DIMENSION_NOTES: Partial<Record<MetricsDimension, string>> = {\n session: 'Session ids are hashed before export. Select a row to filter usage, not to open a session.',\n};\n\nexport interface MetricsReportViewProps {\n report: MetricsReport;\n onFocus: (key: string) => void;\n onPeriodChange?: (period: MetricsPeriod) => void;\n}\n\n/** The same data-only dashboard is rendered by the stories and the settings panel. */\nexport function MetricsReportView({ report, onFocus, onPeriodChange }: MetricsReportViewProps) {\n const label = DIMENSION_LABELS[report.dimension];\n const unattributed = report.groups\n .filter((group) => group.key === 'unknown' || group.key === '')\n .reduce((sum, group) => sum + group.totalTokens, 0);\n const counters = [\n { name: 'Cache reads', value: report.totals.cachedTokens },\n { name: 'Reasoning tokens', value: report.totals.reasoningTokens },\n { name: 'Usage events', value: report.totals.usageEventCount },\n { name: 'Log and span records', value: report.totals.totalRecords },\n ];\n const generated = new Date(report.generatedAt);\n return (\n <div className=\"@container flex w-full min-w-0 flex-col gap-4\" data-testid=\"metrics-report\">\n <MetricsSummary totals={report.totals} />\n <div className=\"grid min-w-0 grid-cols-1 gap-4 @3xl:grid-cols-3\">\n <section\n className=\"flex min-w-0 flex-col gap-4 rounded-lg border border-doom-border bg-doom-panel p-4 @3xl:col-span-2\"\n aria-label=\"Usage over time\"\n >\n <div>\n <h3 className=\"text-lg font-semibold text-doom-hi\">Usage over time</h3>\n <p className=\"mt-1 text-sm text-doom-dim\">Recorded tokens, not estimated spend.</p>\n </div>\n <TimelineChart\n buckets={report.timeline}\n bucketUnit={report.bucketUnit}\n onShorterPeriod={\n report.period === 'day' || onPeriodChange === undefined ? undefined : () => onPeriodChange('day')\n }\n />\n </section>\n <section\n className=\"flex min-w-0 flex-col gap-4 rounded-lg border border-doom-border bg-doom-panel p-4\"\n aria-label=\"Reported counters\"\n >\n <h3 className=\"text-lg font-semibold text-doom-hi\">Reported counters</h3>\n <dl className=\"flex flex-col gap-3\">\n {counters.map((counter) => (\n <div\n key={counter.name}\n className=\"flex flex-wrap items-baseline justify-between gap-x-3 gap-y-1 border-b border-doom-border/40 pb-3\"\n >\n <dt className=\"text-sm text-doom-dim\">{counter.name}</dt>\n <dd\n className=\"text-base font-semibold tabular-nums text-doom-hi\"\n title={counter.value === undefined ? 'Not reported' : String(counter.value)}\n >\n {formatTokens(counter.value ?? Number.NaN)}\n </dd>\n </div>\n ))}\n </dl>\n <p className=\"text-sm leading-relaxed text-doom-dim\">Monetary cost is not included in this metrics report.</p>\n <details className=\"text-sm leading-relaxed text-doom-dim\">\n <summary className=\"cursor-pointer rounded-sm focus-visible:outline focus-visible:outline-1 focus-visible:outline-doom-blue\">\n Why the counters may not add up\n </summary>\n <p className=\"mt-2\">\n The total is reported by providers. Input, output, cache reads and reasoning are independent counters and\n may overlap or be incomplete. They are not a breakdown and may not sum to the total. A gap is not evidence\n of cache traffic. Missing fields say Not reported; a recorded zero stays zero.\n </p>\n </details>\n </section>\n </div>\n <div className=\"grid min-w-0 grid-cols-1 items-start gap-4 @3xl:grid-cols-2\">\n <section\n className=\"flex min-w-0 flex-col gap-3 rounded-lg border border-doom-border bg-doom-panel p-4\"\n aria-label={`Tokens by ${label}`}\n >\n <div>\n <h3 className=\"text-lg font-semibold text-doom-hi\">Tokens by {label}</h3>\n <p className=\"mt-1 text-sm text-doom-dim\">Largest recorded consumers. Select a named row to filter.</p>\n </div>\n {unattributed > 0 ? (\n <div\n className=\"rounded-md border border-doom-edge-yellow bg-doom-tint-yellow p-3\"\n data-testid=\"metrics-attribution-notice\"\n >\n <p className=\"text-sm font-semibold text-doom-yellow\">\n {label.charAt(0).toUpperCase() + label.slice(1)} attribution missing\n </p>\n <p className=\"mt-1 text-sm leading-relaxed text-doom-dim\">\n {formatTokens(unattributed)} tokens have no {label} recorded. They are included in the total, but cannot\n be assigned to a named {label}.\n </p>\n </div>\n ) : null}\n {DIMENSION_NOTES[report.dimension] === undefined ? null : (\n <p className=\"text-sm leading-relaxed text-doom-dim\">{DIMENSION_NOTES[report.dimension]}</p>\n )}\n <GroupBars\n key={`${report.dimension}:${report.focus ?? ''}`}\n groups={report.groups}\n totalTokens={report.totals.totalTokens}\n focus={report.focus}\n onFocus={onFocus}\n />\n <p className=\"text-sm text-doom-dim\">\n Showing {report.groups.length} of {formatTokens(report.totals.groupCount)} {label} groups. Bars show share\n of the reported total.\n </p>\n {report.totals.failedGroups > 0 ? (\n <p className=\"text-sm text-doom-yellow\">\n {report.totals.failedGroups} of {report.totals.groupCount} {label}s failed\n </p>\n ) : null}\n </section>\n <ToolMetrics tools={report.tools} />\n </div>\n <IssuesSection\n key={`${report.generatedAt}:${report.dimension}:${report.focus ?? ''}`}\n tools={report.tools}\n focus={report.dimension === 'session' ? report.focus : undefined}\n count={report.totals.issueCount}\n startTime={report.startTime}\n endTime={report.endTime}\n broaderScope={report.focus !== undefined && report.dimension !== 'session'}\n />\n <footer\n className=\"flex flex-wrap justify-between gap-2 border-t border-doom-border pt-3 text-sm text-doom-dim\"\n data-testid=\"metrics-provenance\"\n >\n <span>\n Source: LogSink ·{' '}\n {report.transport === 'http'\n ? 'HTTP'\n : report.transport === 'worker'\n ? 'database worker'\n : 'transport not reported'}\n </span>\n <span title={report.generatedAt}>\n Report generated {Number.isNaN(generated.getTime()) ? report.generatedAt : generated.toLocaleString()}\n </span>\n </footer>\n </div>\n );\n}\n","import { Button, EmptyState } from '@agimon-ai/po-web-components';\n\nimport type { MetricsDimension, MetricsUnavailable, MetricsUnavailableReason } from '../../../../types/webMetrics';\nimport { DIMENSION_LABELS } from './MetricsReportView';\n\n/**\n * Everything the page says instead of, or above, a report.\n *\n * Kept pure and apart from the panel because these are the states that decide\n * whether a reader trusts the numbers, and they are the ones a fetching\n * component makes awkward to assert. The panel decides which state it is in;\n * this decides how each one reads.\n */\n\nconst EMPTY_TITLES: Record<MetricsUnavailableReason, string> = {\n 'no-sink': 'no log sink',\n 'no-data': 'nothing recorded yet',\n 'no-api': 'metrics not installed',\n 'query-error': 'metrics could not be loaded',\n};\n\nexport function EmptyForReason({ response }: { response: MetricsUnavailable }) {\n return (\n <EmptyState title={EMPTY_TITLES[response.unavailable]} description={response.detail} data-testid=\"metrics-empty\" />\n );\n}\n\nexport interface FocusNoticeProps {\n /** What the reader asked to narrow to; empty means they asked for nothing. */\n requested: string;\n /** What the sink echoed back as applied; absent means it ignored the filter. */\n applied: string | undefined;\n dimension: MetricsDimension;\n onClear: () => void;\n}\n\nexport function FocusNotice({ requested, applied, dimension, onClear }: FocusNoticeProps) {\n if (requested === '') return null;\n if (applied === undefined) {\n // The sink answered without echoing the filter, so these are the machine's\n // whole numbers. Saying \"showing model X\" over them would be a lie, so the\n // drill-down is reported as refused instead.\n return (\n <span\n className=\"rounded-md border border-doom-edge-yellow bg-doom-tint-yellow p-3 text-sm text-doom-yellow\"\n data-testid=\"metrics-focus-refused\"\n >\n this log sink does not support narrowing by {DIMENSION_LABELS[dimension]}, so the numbers below are still\n everything\n <Button variant=\"ghost\" size=\"md\" className=\"ml-2\" onClick={onClear}>\n clear\n </Button>\n </span>\n );\n }\n return (\n <span\n className=\"flex flex-wrap items-center gap-2 rounded-md border border-doom-border bg-doom-panel p-3 text-sm text-doom-dim\"\n data-testid=\"metrics-focus\"\n >\n narrowed to <span className=\"min-w-0 break-all text-doom-hi\">{applied}</span>\n <Button variant=\"ghost\" size=\"md\" className=\"ml-2\" onClick={onClear}>\n clear\n </Button>\n </span>\n );\n}\n\n/** First load and filter changes have no report to display yet. */\nexport function MetricsLoading() {\n return (\n <div className=\"flex flex-col gap-4\" role=\"status\" aria-live=\"polite\" data-testid=\"metrics-loading\">\n <span className=\"text-sm text-doom-dim\">Loading recorded metrics...</span>\n <div aria-hidden=\"true\" className=\"grid grid-cols-2 gap-3 @3xl:grid-cols-4\">\n {[0, 1, 2, 3].map((index) => (\n <div\n key={index}\n className=\"h-24 animate-pulse rounded-lg border border-doom-border bg-doom-panel motion-reduce:animate-none\"\n />\n ))}\n </div>\n <div\n aria-hidden=\"true\"\n className=\"h-48 animate-pulse rounded-lg border border-doom-border bg-doom-panel motion-reduce:animate-none\"\n />\n </div>\n );\n}\n","import type { SettingsPanelProps } from '@agimon-ai/po-core/web';\nimport { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@agimon-ai/po-web-components';\nimport { useEffect, useState } from 'react';\n\nimport {\n isMetricsUnavailable,\n METRICS_DIMENSIONS,\n METRICS_PERIODS,\n type MetricsDimension,\n type MetricsPeriod,\n} from '../../../../types/webMetrics';\nimport { fetchMetrics, type MetricsResult } from '../_lib/metricsApi';\nimport { EmptyForReason, FocusNotice, MetricsLoading } from './MetricsNotice';\nimport { DIMENSION_LABELS, MetricsReportView } from './MetricsReportView';\n\nconst PERIOD_LABELS: Record<MetricsPeriod, string> = {\n day: 'Past day',\n week: 'Past week',\n month: 'Past month',\n all: 'All history',\n};\n\n/** Only render a response under the controls that requested it. */\nexport function MetricsPanel(_props: SettingsPanelProps) {\n const [dimension, setDimension] = useState<MetricsDimension>('model');\n const [period, setPeriod] = useState<MetricsPeriod>('week');\n const [focus, setFocus] = useState('');\n const [refreshKey, setRefreshKey] = useState(0);\n const [completed, setCompleted] = useState<{ key: string; result: MetricsResult }>();\n const requestKey = JSON.stringify([dimension, period, focus, refreshKey]);\n\n useEffect(() => {\n const controller = new AbortController();\n void fetchMetrics(dimension, period, focus, controller.signal)\n .then((result) => {\n if (!controller.signal.aborted) setCompleted({ key: requestKey, result });\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted)\n setCompleted({\n key: requestKey,\n result: { error: error instanceof Error ? error.message : 'Metrics could not be loaded.' },\n });\n });\n return () => controller.abort();\n }, [dimension, period, focus, requestKey]);\n\n const result = completed?.key === requestKey ? completed.result : undefined;\n const loading = result === undefined;\n const response = result !== undefined && 'report' in result ? result.report : undefined;\n const report = response === undefined || isMetricsUnavailable(response) ? undefined : response;\n const error = result !== undefined && 'error' in result ? result.error : '';\n\n return (\n <div className=\"@container flex w-full min-w-0 flex-col gap-4\" data-testid=\"metrics-panel\" aria-busy={loading}>\n <div className=\"flex min-w-0 flex-wrap items-end gap-3 rounded-lg border border-doom-border bg-doom-panel p-3\">\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-base font-medium text-doom-text\">Group by</span>\n <Select\n value={dimension}\n onValueChange={(next) => {\n setFocus('');\n setDimension(next as MetricsDimension);\n }}\n >\n <SelectTrigger aria-label=\"Group metrics by\" data-testid=\"metrics-dimension\" className=\"w-40 \">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {METRICS_DIMENSIONS.map((candidate) => (\n <SelectItem key={candidate} value={candidate}>\n {DIMENSION_LABELS[candidate]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-base font-medium text-doom-text\">Period</span>\n <Select value={period} onValueChange={(next) => setPeriod(next as MetricsPeriod)}>\n <SelectTrigger aria-label=\"Metrics period\" data-testid=\"metrics-period\" className=\"w-40 \">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {METRICS_PERIODS.map((candidate) => (\n <SelectItem key={candidate} value={candidate}>\n {PERIOD_LABELS[candidate]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <Button\n variant=\"outline\"\n size=\"md\"\n className=\"ml-auto\"\n onClick={() => setRefreshKey((current) => current + 1)}\n disabled={loading}\n data-testid=\"metrics-refresh\"\n >\n {loading ? 'Loading...' : 'Refresh'}\n </Button>\n </div>\n {loading ? <MetricsLoading /> : null}\n {error === '' ? null : (\n <p\n role=\"alert\"\n className=\"rounded-lg border border-doom-edge-red bg-doom-tint-red p-4 text-sm text-doom-red\"\n data-testid=\"metrics-error\"\n >\n {error} Use Refresh to try again.\n </p>\n )}\n {response !== undefined && isMetricsUnavailable(response) ? <EmptyForReason response={response} /> : null}\n {report === undefined ? null : (\n <>\n <FocusNotice\n requested={focus}\n applied={report.focus}\n dimension={report.dimension}\n onClear={() => setFocus('')}\n />\n <MetricsReportView report={report} onFocus={setFocus} onPeriodChange={setPeriod} />\n </>\n )}\n </div>\n );\n}\n","import { defineSettingsPanel } from '@agimon-ai/po-core/web';\n\nimport { MetricsPanel } from './_components/MetricsPanel';\nexport default defineSettingsPanel({\n label: 'metrics',\n detail: 'recorded usage, token attribution and issues',\n component: MetricsPanel,\n});\n","import type { ToolMessageRenderProps } from '@agimon-ai/po-core/web';\nimport {\n collapseLines,\n MessageItem,\n MessageItemBody,\n MessageItemHeader,\n MessageItemStatus,\n MessageLines,\n toolTone,\n} from '@agimon-ai/po-web-components';\n\nexport function AgentDiagnosticCard({ toolName, output, running, isError }: ToolMessageRenderProps) {\n const lines = output\n .split('\\n')\n .filter(Boolean)\n .map((text) => ({ text, tone: 'text' as const }));\n return (\n <MessageItem tone={toolTone({ running, isError })} expandable={lines.length > 12}>\n {({ expanded }) => {\n const view = collapseLines(lines, 12, expanded);\n return (\n <>\n <MessageItemHeader title={toolName}>Captured agent evidence</MessageItemHeader>\n <MessageItemBody>\n <MessageLines lines={view.shown} />\n {view.hidden > 0 ? <MessageItemStatus expands>{view.hidden} more lines</MessageItemStatus> : null}\n {running ? <MessageItemStatus tone=\"running\">Reading session telemetry</MessageItemStatus> : null}\n {isError ? <MessageItemStatus tone=\"error\">Diagnostic unavailable</MessageItemStatus> : null}\n </MessageItemBody>\n </>\n );\n }}\n </MessageItem>\n );\n}\n","import { defineToolRenderer } from '@agimon-ai/po-core/web';\n\nimport { AgentDiagnosticCard } from './_components/AgentDiagnosticCard';\n\nexport default defineToolRenderer({ tools: ['diagnose_agent'], message: AgentDiagnosticCard });\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/po-core/web';\n\nimport settingMetrics from '../src/extensions/(frontend)/setting/metrics.panel.web';\nimport toolDiagnoseAgent from '../src/extensions/workspaces/sessions/(frontend)/tool/diagnose-agent.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n id: 'log',\n global: {\n get settingsPanels(): WebPluginContributions['settingsPanels'] { return [via({ id: 'metrics' }, settingMetrics)]; },\n },\n session: {\n get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['diagnose_agent'] }, toolDiagnoseAgent)]; },\n },\n});\n"],"mappings":"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"}
@@ -0,0 +1,185 @@
1
+ //#region src/services/metrics/index.d.ts
2
+ /**
3
+ * Session-local aggregation of the log records the Pi telemetry extension
4
+ * already emits.
5
+ *
6
+ * The aggregator is fed `(recordName, attributes)` pairs rather than Pi events
7
+ * so it can only ever see what the sink sees: no extra instrumentation is added
8
+ * for metrics, and nothing here depends on a live sink. Everything is scoped to
9
+ * the current process, so there is no persistence and no query API.
10
+ */
11
+ /** Record names the aggregator derives numbers from. */
12
+ declare const TOOL_RESULT_RECORD = "pi.tool_result";
13
+ declare const API_ERROR_RECORD = "pi.api_error";
14
+ declare const TURN_FINISHED_RECORD = "pi.turn.finished";
15
+ declare const TURN_FAILED_RECORD = "pi.turn.failed";
16
+ declare const TOOL_TOKEN_SAMPLE_RECORD = "pi.tool_token_sample";
17
+ /** Maximum entries retained in each cardinality-bearing metrics map, including overflow. */
18
+ declare const MAX_RETAINED_METRIC_NAMES = 128;
19
+ /** Snapshot row used to account for names that arrive after a metrics map reaches its bound. */
20
+ declare const METRIC_OVERFLOW_NAME = "__overflow__";
21
+ /** Bucket ceiling per tool: covers 1ms to roughly an hour at 10% resolution. */
22
+ declare const MAX_RETAINED_STATE_PER_TOOL = 160;
23
+ /**
24
+ * Token distributions reuse the same histogram at a higher ceiling: a turn's
25
+ * total can run to millions once cache reads are counted, which the latency
26
+ * ceiling would saturate and report as a flat maximum.
27
+ */
28
+ declare const MAX_RETAINED_TOKEN_BUCKETS = 200;
29
+ interface LogMetricsTokenTotals {
30
+ input: number;
31
+ output: number;
32
+ total: number;
33
+ /**
34
+ * Cached prompt tokens billed at the read rate. Held separately from `input`
35
+ * because the ratio between them is the session's cache hit rate, which is
36
+ * the single largest cost lever a user controls.
37
+ */
38
+ cacheRead: number;
39
+ cacheWrite: number;
40
+ }
41
+ interface LogMetricsToolLatency {
42
+ name: string;
43
+ calls: number;
44
+ p95Ms?: number;
45
+ /** Buckets currently held for this tool, bounded by MAX_RETAINED_STATE_PER_TOOL. */
46
+ retainedStateSize: number;
47
+ }
48
+ interface LogMetricsEventCount {
49
+ name: string;
50
+ count: number;
51
+ }
52
+ interface LogMetricsOperationDuration {
53
+ name: string;
54
+ calls: number;
55
+ p95Ms?: number;
56
+ retainedStateSize: number;
57
+ }
58
+ interface LogMetricsError {
59
+ at: number;
60
+ event: string;
61
+ message: string;
62
+ code: string;
63
+ }
64
+ /**
65
+ * What a tool costs, rather than how long it takes.
66
+ *
67
+ * `p90Tokens` is the 90th percentile of the *turn* total for turns this tool
68
+ * took part in, which is the only attribution the Pi extension emits
69
+ * (`token_attribution: 'toolCallTurn'`). It answers "when this tool runs, how
70
+ * expensive is the turn", not "this tool consumed N tokens" — a turn with
71
+ * several tool calls attributes its total to each of them.
72
+ */
73
+ interface LogMetricsToolCost {
74
+ name: string;
75
+ calls: number;
76
+ failed: number;
77
+ /** Turns carrying usage that this tool took part in, i.e. the p90 sample count. */
78
+ samples: number;
79
+ p90Tokens?: number;
80
+ }
81
+ interface LogMetricsSnapshot {
82
+ events: number;
83
+ errors: number;
84
+ toolCalls: number;
85
+ failedToolCalls: number;
86
+ /** Turns that reported usage, i.e. the denominator for per-turn averages. */
87
+ turns: number;
88
+ abortedTurns: number;
89
+ failedTurns: number;
90
+ tokens: LogMetricsTokenTotals;
91
+ cost: number;
92
+ toolLatency: LogMetricsToolLatency[];
93
+ toolCost: LogMetricsToolCost[];
94
+ failureCategories: LogMetricsEventCount[];
95
+ eventVolume: LogMetricsEventCount[];
96
+ packageEvents: LogMetricsEventCount[];
97
+ sanitizedFailures: number;
98
+ operationDuration: LogMetricsOperationDuration[];
99
+ recentErrors: LogMetricsError[];
100
+ }
101
+ interface LogMetricsAggregatorOptions {
102
+ now?: () => number;
103
+ maxRecentErrors?: number;
104
+ /** Per-map cardinality bound for tests, capped at MAX_RETAINED_METRIC_NAMES. */
105
+ maxMetricNames?: number;
106
+ }
107
+ /** The slice of the aggregator the telemetry extension needs. */
108
+ interface LogMetricsRecorder {
109
+ record(name: string, attributes: Record<string, unknown>): void;
110
+ }
111
+ declare class LogMetricsAggregator implements LogMetricsRecorder {
112
+ private events;
113
+ private errors;
114
+ private toolCalls;
115
+ private failedToolCalls;
116
+ private turns;
117
+ private abortedTurns;
118
+ private failedTurns;
119
+ private cost;
120
+ private readonly tokens;
121
+ private readonly eventCounts;
122
+ private readonly packageCounts;
123
+ /** Shared admission registry keeps the union of every tool-related map within one bound. */
124
+ private readonly toolNames;
125
+ private readonly tools;
126
+ private readonly toolTokens;
127
+ private readonly toolFailures;
128
+ private readonly failureCategories;
129
+ private readonly operations;
130
+ private sanitizedFailures;
131
+ private readonly errorLog;
132
+ private readonly now;
133
+ private readonly maxRecentErrors;
134
+ private readonly maxMetricNames;
135
+ constructor(options?: LogMetricsAggregatorOptions);
136
+ record(name: string, attributes: Record<string, unknown>): void;
137
+ snapshot(): LogMetricsSnapshot;
138
+ /**
139
+ * Ranked by cost per call rather than call count: a tool invoked twice into a
140
+ * 300k-token turn is the one worth changing, and a frequency ranking buries
141
+ * it under the cheap tools that dominate any transcript.
142
+ */
143
+ private toolCostRows;
144
+ private boundedToolName;
145
+ private countCategory;
146
+ private recordOperationDuration;
147
+ private recordSanitizedFailure;
148
+ private recordToolResult;
149
+ /**
150
+ * Attributes a turn's token total to every tool that ran in it. The Pi
151
+ * extension emits one sample per tool result carrying the whole turn's usage,
152
+ * so this builds a per-tool distribution of "how expensive is a turn that
153
+ * uses this tool" — see LogMetricsToolCost for what that does and does not
154
+ * claim.
155
+ */
156
+ private recordToolTokenSample;
157
+ private recordApiError;
158
+ private recordTurnUsage;
159
+ /** A turn the user has to redo: the most expensive failure mode there is. */
160
+ private recordTurnFailure;
161
+ private pushError;
162
+ }
163
+ //#endregion
164
+ //#region src/services/findings/index.d.ts
165
+ type LogMetricsFindingSeverity = 'critical' | 'warning' | 'info';
166
+ interface LogMetricsFinding {
167
+ /** Stable identifier, so a finding can be asserted without matching prose. */
168
+ id: string;
169
+ severity: LogMetricsFindingSeverity;
170
+ /** What the finding is about: a tool name, or a facet like `cache`. */
171
+ subject: string;
172
+ /** The evidence, already formatted. */
173
+ detail: string;
174
+ /** The change that moves the number. */
175
+ action: string;
176
+ }
177
+ /**
178
+ * Ranked most-actionable first. Severity leads because a failing tool costs
179
+ * more than an inefficient one, and the panel is read top-down under a
180
+ * height limit.
181
+ */
182
+ declare function deriveFindings(snapshot: LogMetricsSnapshot): LogMetricsFinding[];
183
+ //#endregion
184
+ export { TURN_FINISHED_RECORD as S, MAX_RETAINED_TOKEN_BUCKETS as _, LogMetricsAggregator as a, TOOL_TOKEN_SAMPLE_RECORD as b, LogMetricsEventCount as c, LogMetricsSnapshot as d, LogMetricsTokenTotals as f, MAX_RETAINED_STATE_PER_TOOL as g, MAX_RETAINED_METRIC_NAMES as h, API_ERROR_RECORD as i, LogMetricsOperationDuration as l, LogMetricsToolLatency as m, LogMetricsFindingSeverity as n, LogMetricsAggregatorOptions as o, LogMetricsToolCost as p, deriveFindings as r, LogMetricsError as s, LogMetricsFinding as t, LogMetricsRecorder as u, METRIC_OVERFLOW_NAME as v, TURN_FAILED_RECORD as x, TOOL_RESULT_RECORD as y };
185
+ //# sourceMappingURL=index-CTZMUfTZ.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-CTZMUfTZ.d.mts","names":[],"sources":["../src/services/metrics/index.ts","../src/services/findings/index.ts"],"mappings":";;;;;;;;;;;cAWa;cACA;cACA;cACA;cACA;;cAOA;;cAEA;;cAgCA;;;;;;cAOA;UAEI;EACf;EACA;EACA;;;;;;EAMA;EACA;;UAGe;EACf;EACA;EACA;;EAEA;;UAGe;EACf;EACA;;UAGe;EACf;EACA;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA;;;;;;;;;;;UAYe;EACf;EACA;EACA;;EAEA;EACA;;UAGe;EACf;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA,QAAQ;EACR;EACA,aAAa;EACb,UAAU;EACV,mBAAmB;EACnB,aAAa;EACb,eAAe;EACf;EACA,mBAAmB;EACnB,cAAc;;UAGC;EACf;EACA;;EAEA;;;UAIe;EACf,OAAO,cAAc,YAAY;;cAoGtB,gCAAgC;UACnC;UACA;UACA;UACA;UACA;UACA;UACA;UACA;mBACS;mBACA;mBACA;;mBAEA;mBACA;mBACA;mBACA;mBACA;mBACA;UACT;mBACS;mBACA;mBACA;mBACA;EAEjB,YAAY,UAAS;EASrB,OAAO,cAAc,YAAY;EAkBjC,YAAY;;;;;;UA2DJ;UAiBA;UAMA;UAIA;UAoBA;UAiBA;;;;;;;;UAiCA;UASA;UAQA;;UAWA;UAOA;;;;KCheE;UAEK;;EAEf;EACA,UAAU;;EAEV;;EAEA;;EAEA;;;;;;;iBAuKc,eAAe,UAAU,qBAAqB"}
package/dist/index.cjs ADDED
@@ -0,0 +1,13 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_metrics = require("./metrics-jCZe13Xg.cjs");
3
+ exports.API_ERROR_RECORD = require_metrics.API_ERROR_RECORD;
4
+ exports.LogMetricsAggregator = require_metrics.LogMetricsAggregator;
5
+ exports.MAX_RETAINED_METRIC_NAMES = require_metrics.MAX_RETAINED_METRIC_NAMES;
6
+ exports.MAX_RETAINED_STATE_PER_TOOL = require_metrics.MAX_RETAINED_STATE_PER_TOOL;
7
+ exports.MAX_RETAINED_TOKEN_BUCKETS = require_metrics.MAX_RETAINED_TOKEN_BUCKETS;
8
+ exports.METRIC_OVERFLOW_NAME = require_metrics.METRIC_OVERFLOW_NAME;
9
+ exports.TOOL_RESULT_RECORD = require_metrics.TOOL_RESULT_RECORD;
10
+ exports.TOOL_TOKEN_SAMPLE_RECORD = require_metrics.TOOL_TOKEN_SAMPLE_RECORD;
11
+ exports.TURN_FAILED_RECORD = require_metrics.TURN_FAILED_RECORD;
12
+ exports.TURN_FINISHED_RECORD = require_metrics.TURN_FINISHED_RECORD;
13
+ exports.deriveFindings = require_metrics.deriveFindings;
@@ -0,0 +1,36 @@
1
+ import { S as TURN_FINISHED_RECORD, _ as MAX_RETAINED_TOKEN_BUCKETS, a as LogMetricsAggregator, b as TOOL_TOKEN_SAMPLE_RECORD, c as LogMetricsEventCount, d as LogMetricsSnapshot, f as LogMetricsTokenTotals, g as MAX_RETAINED_STATE_PER_TOOL, h as MAX_RETAINED_METRIC_NAMES, i as API_ERROR_RECORD, l as LogMetricsOperationDuration, m as LogMetricsToolLatency, n as LogMetricsFindingSeverity, o as LogMetricsAggregatorOptions, p as LogMetricsToolCost, r as deriveFindings, s as LogMetricsError, t as LogMetricsFinding, u as LogMetricsRecorder, v as METRIC_OVERFLOW_NAME, x as TURN_FAILED_RECORD, y as TOOL_RESULT_RECORD } from "./index-CTZMUfTZ.mjs";
2
+ import { i as MetricsSource } from "./metricsSource-cZnErdv4.mjs";
3
+ import { DoomTelemetryOptions } from "@agimon-ai/po-telemetry";
4
+ //#region src/types/sinkStatus.d.ts
5
+ interface SinkStatus {
6
+ service: string;
7
+ backend: string;
8
+ endpoint: string;
9
+ endpointSource: string;
10
+ traces: boolean;
11
+ redaction: boolean;
12
+ fileFallback: boolean;
13
+ }
14
+ //#endregion
15
+ //#region src/types/piTelemetry.d.ts
16
+ type TelemetryFactory = NonNullable<DoomTelemetryOptions['telemetryFactory']>;
17
+ interface PiTelemetryExtensionOptions {
18
+ allowFileFallback?: boolean;
19
+ /** Overrides the telemetry service identity. */
20
+ serviceName?: string;
21
+ /** Overrides the leader contribution owner. */
22
+ leaderSource?: string;
23
+ env?: NodeJS.ProcessEnv;
24
+ telemetryFactory?: TelemetryFactory;
25
+ /** Receives every sanitized record, whether or not a sink is live. */
26
+ metrics?: LogMetricsRecorder;
27
+ /** Called once the sink backend is resolved, so the overlay can report it. */
28
+ onSinkStatus?: (status: SinkStatus | undefined) => void;
29
+ /** Overrides the sink-history transport; injected in tests. */
30
+ metricsSource?: MetricsSource;
31
+ /** Receives telemetry lifecycle failures without writing into the TUI. */
32
+ onDiagnostic?: (message: string) => void;
33
+ }
34
+ //#endregion
35
+ export { API_ERROR_RECORD, LogMetricsAggregator, type LogMetricsAggregatorOptions, type LogMetricsError, type LogMetricsEventCount, type LogMetricsFinding, type LogMetricsFindingSeverity, type LogMetricsOperationDuration, type LogMetricsRecorder, type LogMetricsSnapshot, type LogMetricsTokenTotals, type LogMetricsToolCost, type LogMetricsToolLatency, MAX_RETAINED_METRIC_NAMES, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, METRIC_OVERFLOW_NAME, type PiTelemetryExtensionOptions, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD, deriveFindings };
36
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/sinkStatus.ts","../src/types/piTelemetry.ts"],"mappings":";;;;UAAiB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;;;;KCFU,mBAAmB,YAAY;UAI1B;EACf;;EAEA;;EAEA;EACA,MAAM,OAAO;EACb,mBAAmB;;EAEnB,UAAU;;EAEV,gBAAgB,QAAQ;;EAExB,gBAAgB;;EAEhB,gBAAgB"}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { a as MAX_RETAINED_TOKEN_BUCKETS, c as TOOL_TOKEN_SAMPLE_RECORD, d as deriveFindings, i as MAX_RETAINED_STATE_PER_TOOL, l as TURN_FAILED_RECORD, n as LogMetricsAggregator, o as METRIC_OVERFLOW_NAME, r as MAX_RETAINED_METRIC_NAMES, s as TOOL_RESULT_RECORD, t as API_ERROR_RECORD, u as TURN_FINISHED_RECORD } from "./metrics-DywCpDJ_.mjs";
2
+ export { API_ERROR_RECORD, LogMetricsAggregator, MAX_RETAINED_METRIC_NAMES, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, METRIC_OVERFLOW_NAME, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD, deriveFindings };