opencode-usage-insights 0.0.0-stage → 0.1.1

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.
@@ -0,0 +1,22 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/tui.tsx", "../src/core.ts", "../src/text.ts", "../src/line.ts", "../src/stats/queries.ts", "../src/panel-data.ts", "../src/panel.tsx", "../src/stats/aggregates.ts", "../src/panel-build.ts", "../src/stats/maintenance.ts", "../src/stats/db.ts", "../src/stats/paths.ts", "../src/sidebar.tsx"],
4
+ "sourcesContent": [
5
+ "import { use as _$use } from \"@opentui/solid\";\nimport { createElement as _$createElement } from \"@opentui/solid\";\nimport { createComponent as _$createComponent } from \"@opentui/solid\";\n/** @jsxImportSource @opentui/solid */\nimport { Plugin } from \"@opencode/plugin/tui\";\nimport { StyledText, fg } from \"@opentui/core\";\nimport { Show, createMemo, createRenderEffect, createSignal } from \"solid-js\";\nimport { computeTurn } from \"./core\";\nimport { lineParts, lineText, sessionTotals, toTranscriptTail } from \"./line\";\nimport { DEFAULT_SETTINGS, PANEL_NAME, parseArgs, sanitizeSettings } from \"./panel-data\";\nimport { Panel } from \"./panel\";\nimport { TurnsSidebar } from \"./sidebar\";\nimport { openStatsDbReadOnly, openStatsDbWrite } from \"./stats/db\";\nimport { resolveDbPath } from \"./stats/paths\";\n\n/**\n * TUI side of the plugin. Two contributions:\n *\n * - `session.composer.top`: one telemetry line after every turn with the end\n * time, the delta since the previous prompt, the provider and model that ran\n * it, turn and session token totals, and cache warnings.\n * - `session.panel` + the `/stats` slash command: the statistics panel over\n * the database written by the server entry.\n *\n * The line is computed from the host's transcript alone; the database is opened\n * lazily for the panel and the sidebar, read-only in spirit: the TUI never\n * writes usage. When the database cannot be opened, the line still works and\n * the panel explains itself.\n */\n\n/**\n * Shortest interval between two panel rebuilds. A tool-call loop ends several\n * steps in a row, and each one used to rebuild every open view; the views read\n * the database, so the burst is coalesced into one rebuild.\n */\nconst REFRESH_THROTTLE_MS = 300;\n\n// Test hook. The plugin loader reads the default export only; named exports\n// are ignored.\nexport const __test = {\n line: undefined,\n lineText: undefined,\n open: undefined,\n state: undefined\n};\nexport default Plugin.define({\n id: \"usage-insights\",\n setup(ctx) {\n const options = ctx.options ?? {};\n if (options.enabled === false) return;\n const panelEnabled = options.panel !== false;\n\n // Panel state lives in the host's durable store, so the view, range, sort,\n // bucket and filters survive a restart and the command palette and the\n // panel keys mutate the same object.\n const [settings, updateSettings] = ctx.storage.store(\"panel\", {\n initial: DEFAULT_SETTINGS\n });\n const patchSettings = mutation => {\n void updateSettings(mutation);\n };\n // Bumped when a step or an execution ends: the panel and the sidebar read\n // the database, which is not reactive by itself.\n const [refresh, setRefresh] = createSignal(0);\n let database;\n let databaseFailed = false;\n let refreshTimer;\n let lastRefresh = 0;\n\n /**\n * Ask the panel and the sidebar to re-read the database, at most once per\n * throttle window. The first event after an idle stretch is instant; the\n * rest of a burst is folded into one trailing rebuild.\n */\n function bumpRefresh() {\n const now = Date.now();\n const wait = lastRefresh + REFRESH_THROTTLE_MS - now;\n if (wait <= 0) {\n lastRefresh = now;\n setRefresh(value => value + 1);\n return;\n }\n if (refreshTimer !== undefined) return;\n refreshTimer = setTimeout(() => {\n refreshTimer = undefined;\n lastRefresh = Date.now();\n setRefresh(value => value + 1);\n }, wait)\n // A repaint is not a reason to keep the process alive.\n;\n refreshTimer.unref?.();\n }\n\n /**\n * Reader connection. Read-only on purpose: the TUI only ever reads, and a\n * reader that cannot write never takes the recorder's lock, never runs a\n * schema statement and cannot corrupt what it shows.\n */\n function db() {\n if (database !== undefined) return database;\n if (databaseFailed) return undefined;\n try {\n database = openStatsDbReadOnly(resolveDbPath(options.db));\n return database;\n } catch {\n databaseFailed = true;\n return undefined;\n }\n }\n\n /**\n * Write connection for maintenance only (prune, compact), opened for the\n * duration of the operation with a short busy timeout: a reader waits, but\n * a maintenance action that cannot take the lock is an error the user can\n * see and retry.\n */\n function withWrite(run) {\n let handle;\n try {\n handle = openStatsDbWrite(resolveDbPath(options.db));\n return run(handle);\n } catch {\n return undefined;\n } finally {\n handle?.close();\n }\n }\n function sessionStart(sessionID) {\n return ctx.data.session.get(sessionID)?.time.created;\n }\n\n /**\n * True while the session is running a turn. The host publishes that state\n * from its execution events, so the line only appears when the turn is\n * over — not between the steps of a tool-call loop. A host without the\n * flag degrades to \"idle\", which is the old behaviour.\n */\n function sessionIdle(sessionID) {\n try {\n return ctx.data.session.status(sessionID) === \"idle\";\n } catch {\n return true;\n }\n }\n function lineFor(sessionID) {\n // A running turn has no final numbers yet: the line belongs to finished\n // work, so it stays hidden until the session goes idle again. The check\n // comes first on purpose: the host publishes the message list as a store,\n // so this function is recomputed on every streamed delta, and reading the\n // list there would be per-delta work for a line that is not shown.\n if (!sessionIdle(sessionID)) return undefined;\n const messages = ctx.data.session.message.list(sessionID);\n const info = ctx.data.session.get(sessionID);\n return computeTurn(toTranscriptTail(messages, 2), sessionTotals(info));\n }\n function toneColor(tone) {\n if (tone === \"warning\") return ctx.theme.text.feedback.warning.base;\n if (tone === \"muted\") return ctx.theme.text.muted;\n return ctx.theme.text.base;\n }\n\n /** Structural view of the text renderable: no host-only class is imported. */\n\n function Line(props) {\n const parts = createMemo(() => lineParts(lineFor(props.sessionID), {\n labels: options.labels,\n fields: options.fields,\n turnTotals: options.turnTotals\n }));\n return _$createComponent(Show, {\n get when() {\n return parts().length > 0;\n },\n children: () => {\n // The line is one text renderable whose content is *replaced* on every\n // update. In the current OpenTUI build the `content` prop stringifies a\n // StyledText, and a StyledText child is appended as chunks that are\n // never removed — the line grew one extra copy per reactive update.\n // Assigning the property replaces; a render effect runs before paint.\n let node;\n const element = (() => {\n var _el$ = _$createElement(\"text\");\n _$use(target => {\n node = target;\n }, _el$);\n return _el$;\n })();\n createRenderEffect(() => {\n const rendered = node;\n if (rendered === undefined) return;\n const current = parts();\n rendered.content = current.length === 0 ? \"\" : new StyledText(current.map(part => fg(toneColor(part.tone))(part.text)));\n });\n return element;\n }\n });\n }\n const disposeLine = ctx.ui.slot({\n append: \"session.composer.top\",\n render: input => _$createComponent(Line, {\n get sessionID() {\n return input.sessionID;\n }\n })\n });\n const disposePanel = panelEnabled ? ctx.ui.slot({\n append: \"session.panel\",\n render: input => input.name === PANEL_NAME ? _$createComponent(Panel, {\n input: input,\n ctx: ctx,\n deps: {\n db,\n dbPath: () => resolveDbPath(options.db),\n withWrite,\n now: () => Date.now(),\n sessionStart,\n refresh\n },\n settings: settings,\n update: patchSettings,\n get format() {\n return {\n labels: options.labels,\n fields: options.fields,\n turnTotals: options.turnTotals\n };\n }\n }) : null\n }) : () => {};\n const disposeSidebar = ctx.ui.slot({\n append: \"sidebar.content\",\n render: input => options.sidebar === false ? null : _$createComponent(TurnsSidebar, {\n get sessionID() {\n return input.sessionID;\n },\n ctx: ctx,\n get deps() {\n return {\n db,\n refresh,\n limit: Math.max(1, Math.round(options.sidebarTurns ?? 5))\n };\n },\n get options() {\n return {\n labels: options.labels,\n fields: options.fields,\n turnTotals: options.turnTotals\n };\n }\n })\n });\n\n /**\n * `/usage` toggles the panel. With no argument tokens it closes a panel it\n * opened, so the same command both shows and hides it and the user never\n * has to leave the session to get rid of it. Any argument — even an\n * unknown token, which `parseArgs` ignores — keeps the panel open.\n *\n * A slash command declared with `arguments: true` receives its raw input,\n * which still carries the `/usage` (or `/insights`) token itself, so the\n * command tokens are dropped before deciding.\n */\n function open(input) {\n const parsed = parseArgs(input);\n if (parsed.view !== undefined || parsed.range !== undefined) {\n patchSettings(draft => {\n if (parsed.view !== undefined) draft.view = parsed.view;\n if (parsed.range !== undefined) draft.range = parsed.range;\n });\n }\n const tokens = (input ?? \"\").split(/\\s+/).map(token => token.trim()).filter(token => token !== \"\" && !token.startsWith(\"/\"));\n if (tokens.length === 0 && ctx.ui.panel.current()?.name === PANEL_NAME) {\n ctx.ui.panel.close();\n return true;\n }\n return ctx.ui.panel.open(PANEL_NAME);\n }\n if (panelEnabled) {\n ctx.keymap.layer(() => {\n // The explicit block body avoids a TypeScript TSX parser quirk: a\n // parenthesized object literal as an arrow body can be misread as arrow\n // parameters when a later `) =>` exists in the same file.\n //\n // `mode: \"global\"` is required: without it the layer is pinned to the\n // default `base` input mode, and the host only exposes a layer's palette\n // and slash commands while that mode is active — so `/usage` was\n // invisible in the prompt completion and the command palette.\n return {\n mode: \"global\",\n commands: [{\n id: \"usage-insights.stats\",\n title: \"Usage insights\",\n description: \"Open the usage statistics panel\",\n group: \"Usage insights\",\n bind: false,\n palette: true,\n slash: {\n name: \"usage\",\n aliases: [\"insights\"],\n arguments: true\n },\n run: input => {\n if (!open(input)) {\n ctx.ui.toast.show({\n message: \"Open a session first, then run /usage\",\n variant: \"warning\"\n });\n }\n }\n }]\n };\n });\n }\n const disposeEvents = [\n // A finished step means the history views have one more row: let them\n // re-read the database.\n ctx.data.on(\"session.step.ended\", () => bumpRefresh()), ctx.data.on(\"session.execution.succeeded\", () => bumpRefresh()), ctx.data.on(\"session.execution.failed\", () => bumpRefresh()), ctx.data.on(\"session.execution.interrupted\", () => bumpRefresh()), ctx.data.on(\"session.compaction.ended\", () => bumpRefresh())];\n __test.line = lineFor;\n __test.lineText = sessionID => lineText(lineParts(lineFor(sessionID), {\n labels: options.labels,\n fields: options.fields\n }));\n __test.open = open;\n __test.state = () => {\n const current = sanitizeSettings(settings);\n return {\n view: current.view,\n range: current.range\n };\n };\n return () => {\n if (refreshTimer !== undefined) clearTimeout(refreshTimer);\n for (const dispose of disposeEvents) dispose();\n disposeLine();\n disposePanel();\n disposeSidebar();\n database?.close();\n };\n }\n});",
6
+ "/**\n * Pure turn accounting shared by the TUI line, the server recorder and the\n * stats engine.\n *\n * Everything here operates on structural shapes that mirror the session\n * messages published by OpenCode, so the logic stays testable without a host\n * runtime and without the effect-based schema types.\n */\n\nexport type ModelRefLike = {\n providerID: string\n id: string\n variant?: string | undefined\n}\n\nexport type AssistantMessage = {\n type: \"assistant\"\n id: string\n time?: { created?: number | undefined; streamed?: number | undefined; completed?: number | undefined } | undefined\n model?: ModelRefLike | undefined\n tokens?:\n | {\n input?: number | undefined\n output?: number | undefined\n reasoning?: number | undefined\n cache?: { read?: number | undefined; write?: number | undefined } | undefined\n }\n | undefined\n cost?: number | undefined\n finish?: string | null | undefined\n rawFinish?: string | null | undefined\n}\n\nexport type PromptMessage = {\n type: \"user\" | \"synthetic\" | \"system\"\n id?: string | undefined\n time?: { created?: number | undefined } | undefined\n}\n\nexport type TranscriptMessage =\n | AssistantMessage\n | PromptMessage\n | { type: string; id?: string | undefined; time?: { created?: number | undefined } | undefined }\n\n/** Accumulated token usage for a group of assistant steps. */\nexport type Totals = {\n input: number\n cached: number\n output: number\n reasoning: number\n cacheWrite: number\n cost: number\n steps: number\n}\n\nexport type CacheDrop = {\n messageID: string\n /** Tokens that were expected to hit the prompt cache but did not. */\n tokens: number\n /** `cache.read` of the step that was used as the reuse reference. */\n prevCached: number\n /** Time between the reference step's completion and this step's request. */\n gapMs: number | undefined\n}\n\nexport type TurnStats = {\n /** Wall clock time of the last step's completion. */\n endAt: number | undefined\n /** Creation time of the first settled step of the turn. */\n startAt: number | undefined\n /** Creation time of the prompt that opened this turn. */\n promptAt: number | undefined\n /** Time between the previous prompt and this turn's prompt. */\n promptGapMs: number | undefined\n providerID: string | undefined\n modelID: string | undefined\n variant: string | undefined\n /** Provider ID differs from the previous turn in the same transcript. */\n providerChanged: boolean\n /** Provider is the same but the model or variant differs. */\n modelChanged: boolean\n totals: Totals\n /** Passed through from the caller (session-wide accumulated usage). */\n session: Totals | undefined\n /**\n * Prompt-side usage of the last finished step: the context the turn ended\n * with. Summing `input`/`cached` over the steps of a turn counts the same\n * re-sent prompt once per step, which is not what a context view means.\n */\n latest: Totals\n drops: CacheDrop[]\n cacheDropTokens: number\n cacheExpired: boolean\n /** Assistant steps in the turn. */\n steps: number\n}\n\nexport type TurnSegment = {\n assistants: AssistantMessage[]\n prompt: TranscriptMessage | undefined\n}\n\n/** Message types that open a turn: a run of assistants belongs to the last one. */\nexport const PROMPT_BOUNDARIES: ReadonlySet<string> = new Set([\"user\", \"synthetic\", \"system\"])\n\n/**\n * Split a transcript into turns. A turn is a run of assistant steps opened by\n * a prompt boundary (`user`, `synthetic`, `system`). Idle notices, info\n * messages, compactions and shell entries keep the current turn open so a\n * single execution is never split in two.\n */\nexport function transcriptSegments(messages: readonly TranscriptMessage[]): TurnSegment[] {\n const segments: TurnSegment[] = []\n let current: AssistantMessage[] = []\n let prompt: TranscriptMessage | undefined\n\n for (const message of messages) {\n if (message.type === \"assistant\") {\n current.push(message as AssistantMessage)\n continue\n }\n if (!PROMPT_BOUNDARIES.has(message.type)) continue\n if (current.length > 0) {\n segments.push({ assistants: current, prompt })\n current = []\n }\n prompt = message\n }\n if (current.length > 0) segments.push({ assistants: current, prompt })\n return segments\n}\n\n/**\n * The last `count` segments, found by walking the transcript backwards. The\n * live line only needs the turn that just ended and the one before it, and a\n * long session holds tens of thousands of messages: the tail walk stops as\n * soon as it has them, instead of building a segment for every turn.\n */\nexport function transcriptTail(messages: readonly TranscriptMessage[], count: number): TurnSegment[] {\n const segments: TurnSegment[] = []\n let current: AssistantMessage[] = []\n\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]!\n if (message.type === \"assistant\") {\n current.unshift(message as AssistantMessage)\n continue\n }\n if (!PROMPT_BOUNDARIES.has(message.type)) continue\n if (current.length === 0) continue\n // The boundary closes the run collected after it.\n segments.unshift({ assistants: current, prompt: message })\n if (segments.length >= count) return segments\n current = []\n }\n // A leading run with no prompt before it is a segment of its own.\n if (current.length > 0 && segments.length < count) segments.unshift({ assistants: current, prompt: undefined })\n return segments\n}\n\nexport function tokenCounts(message: AssistantMessage): { input: number; output: number; reasoning: number; read: number; write: number } {\n const tokens = message.tokens\n return {\n input: tokens?.input ?? 0,\n output: tokens?.output ?? 0,\n reasoning: tokens?.reasoning ?? 0,\n read: tokens?.cache?.read ?? 0,\n write: tokens?.cache?.write ?? 0,\n }\n}\n\nexport function totalize(steps: readonly AssistantMessage[]): Totals {\n const totals: Totals = { input: 0, cached: 0, output: 0, reasoning: 0, cacheWrite: 0, cost: 0, steps: steps.length }\n for (const step of steps) {\n const counts = tokenCounts(step)\n totals.input += counts.input\n totals.cached += counts.read\n totals.output += counts.output\n totals.reasoning += counts.reasoning\n totals.cacheWrite += counts.write\n totals.cost += step.cost ?? 0\n }\n return totals\n}\n\n/**\n * The identity of a model reference: the provider it runs through, the model\n * and its variant. Two steps with the same key ran the same thing the same way,\n * which is what `modelChanged` compares.\n */\nexport function identityKey(model: ModelRefLike | undefined): string | undefined {\n if (!model) return undefined\n return `${model.providerID}/${model.id}${model.variant ? `#${model.variant}` : \"\"}`\n}\n\nexport function cacheRead(message: AssistantMessage | undefined): number {\n return message?.tokens?.cache?.read ?? 0\n}\n\nexport function stepCreated(message: AssistantMessage | undefined): number | undefined {\n return message?.time?.created\n}\n\nexport function stepEnd(message: AssistantMessage | undefined): number | undefined {\n const time = message?.time\n if (!time) return undefined\n return time.completed ?? time.streamed ?? time.created\n}\n\n/**\n * Which identity the two steps must share for a reuse loss to be measurable.\n * `provider` is the strict one: two providers are two caches, so a switch\n * between them is not a loss but a different cache. `model` drops that test,\n * for the reports that measure what an identity change costs: forfeiting the\n * previous provider's cache is exactly the cost they report.\n */\nexport type CacheDropScope = \"provider\" | \"model\"\n\n/**\n * Cache reuse loss against a reference step, mirroring OpenCode's own\n * `turn-usage` heuristic: only comparable when the model is identical, and\n * OpenAI's API reports small reads that are not worth flagging.\n */\nexport function cacheDrop(reference: AssistantMessage | undefined, current: AssistantMessage, scope: CacheDropScope = \"provider\"): number | undefined {\n if (!reference) return undefined\n const before = reference.model\n const after = current.model\n if (!before || !after) return undefined\n if (scope === \"provider\" && before.providerID !== after.providerID) return undefined\n if (before.id !== after.id || (before.variant ?? \"\") !== (after.variant ?? \"\")) {\n return undefined\n }\n const drop = cacheRead(reference) - cacheRead(current)\n if (isNoiseDrop(after.providerID, drop)) return undefined\n return drop > 0 ? drop : undefined\n}\n\n/** Milliseconds between a reference step's completion and the next request. */\nexport function stepGap(reference: AssistantMessage | undefined, current: AssistantMessage): number | undefined {\n const ended = stepEnd(reference)\n const started = stepCreated(current)\n if (ended === undefined || started === undefined) return undefined\n return Math.max(0, started - ended)\n}\n\n/**\n * Streaming duration of a step: request start to the last streamed chunk. The\n * host's `streamed` stamp marks the end of streaming, so this is the window a\n * generation rate belongs to — not a time to first token, which the session\n * messages do not record.\n */\nexport function stepStreamMs(message: AssistantMessage): number | undefined {\n const created = message.time?.created\n const streamed = message.time?.streamed\n if (created === undefined || streamed === undefined || streamed <= created) return undefined\n return streamed - created\n}\n\n/**\n * Tokens produced per second over a window. For a step the window is the\n * streaming one: request start to the last streamed chunk (`created` →\n * `streamed`) — `completed` marks the end of the step, which for a tool-call\n * step happens after the tool calls were handled. Returns undefined when the\n * window or the produced tokens are unknown. Single definition, shared by the\n * recorder (which stores it per step) and the latency view.\n */\nexport function tokensPerSecond(produced: number, from: number | undefined, to: number | undefined): number | undefined {\n if (from === undefined || to === undefined || to <= from) return undefined\n if (produced <= 0) return undefined\n return (produced / (to - from)) * 1000\n}\n\n\n/** Output-plus-reasoning tokens per second of a step. */\nexport function stepTps(message: AssistantMessage): number | undefined {\n const counts = tokenCounts(message)\n return tokensPerSecond(counts.output + counts.reasoning, message.time?.created, message.time?.streamed)\n}\n\nconst DROP_EXEMPT_PROVIDERS = new Set([\"openai\"])\n\n/**\n * True when a step has final numbers. A step that is still streaming reports\n * partial tokens (usually zero) and no cache read yet, so reading it would\n * render an empty line and invent a cache-expiry warning against it.\n */\nexport function isSettledStep(message: AssistantMessage): boolean {\n if (message.time?.completed !== undefined) return true\n return message.finish !== undefined && message.finish !== null\n}\n\n/**\n * Compute the stats of the most recent turn in a transcript, including the\n * comparison against the previous turn (a provider or model switch, and cache\n * reuse).\n *\n * Returns undefined while the newest turn is still being generated: the line\n * appears when a turn ends, never with the zeroes of an in-flight response.\n */\nexport function computeTurn(messages: readonly TranscriptMessage[], session?: Totals): TurnStats | undefined {\n const segments = transcriptTail(messages, 2)\n const last = segments[segments.length - 1]\n if (!last) return undefined\n const previous = segments[segments.length - 2]\n const lastStep = last.assistants[last.assistants.length - 1]\n if (!lastStep) return undefined\n if (!isSettledStep(lastStep)) return undefined\n const previousStep = previous?.assistants[previous.assistants.length - 1]\n // Defensive: a step that never settled mid-turn carries no usable numbers,\n // so it is neither a cache reference nor a reuse candidate.\n const settled = last.assistants.filter(isSettledStep)\n\n const drops: CacheDrop[] = []\n let reference: AssistantMessage | undefined = previousStep !== undefined && isSettledStep(previousStep) ? previousStep : undefined\n for (const step of settled) {\n const drop = cacheDrop(reference, step)\n if (drop !== undefined) {\n drops.push({\n messageID: step.id,\n tokens: drop,\n prevCached: cacheRead(reference),\n gapMs: stepGap(reference, step),\n })\n }\n reference = step\n }\n\n const provider = lastStep.model?.providerID\n const previousProvider = previousStep?.model?.providerID\n const key = identityKey(lastStep.model)\n const previousKey = identityKey(previousStep?.model)\n\n return {\n endAt: stepEnd(lastStep),\n startAt: stepCreated(settled[0] ?? lastStep),\n promptAt: last.prompt?.time?.created,\n promptGapMs: promptGap(last.prompt, previous?.prompt),\n providerID: provider,\n modelID: lastStep.model?.id,\n variant: lastStep.model?.variant,\n providerChanged: previousProvider !== undefined && provider !== undefined && previousProvider !== provider,\n modelChanged: previousKey !== undefined && key !== undefined && previousKey !== key && previousProvider === provider,\n totals: totalize(settled),\n latest: totalize([lastStep]),\n session,\n drops,\n cacheDropTokens: drops.reduce((sum, drop) => sum + drop.tokens, 0),\n cacheExpired: drops.length > 0,\n steps: settled.length,\n }\n}\n\nfunction promptGap(current: TranscriptMessage | undefined, previous: TranscriptMessage | undefined): number | undefined {\n const now = current?.time?.created\n const before = previous?.time?.created\n if (now === undefined || before === undefined) return undefined\n return Math.max(0, now - before)\n}\n\n/** True when a cache drop is small enough to be provider noise. */\nexport function isNoiseDrop(providerID: string | undefined, drop: number): boolean {\n if (providerID === undefined) return false\n return DROP_EXEMPT_PROVIDERS.has(providerID) && drop >= 1024 && drop <= 2048\n}\n\n// ---------------------------------------------------------------------------\n// Formatting\n// ---------------------------------------------------------------------------\n\nexport type LineTone = \"base\" | \"muted\" | \"warning\"\n\nexport type LineSegment = { text: string; tone: LineTone }\n\nexport type LineLabels = {\n delta: string\n turn: string\n session: string\n step: string\n steps: string\n input: string\n cached: string\n output: string\n reasoning: string\n cost: string\n cacheExpired: string\n providerChanged: string\n modelChanged: string\n}\n\n/**\n * How the turn segment counts prompt usage. `latest` shows the prompt size the\n * turn ended with — the context view, and what OpenCode's own turn-token debug\n * line reports. `sum` adds every step of the turn: the billing view, where a\n * tool-call loop re-sends (and re-reads) the same context once per step.\n */\nexport type TurnTotalsView = \"latest\" | \"sum\"\n\nexport type LineFields = {\n clock: boolean\n delta: boolean\n model: boolean\n variant: boolean\n turn: boolean\n session: boolean\n reasoning: boolean\n cost: boolean\n cacheExpiry: boolean\n}\n\nexport const DEFAULT_LABELS: LineLabels = {\n delta: \"Δ\",\n // The turn segment is unlabelled by default to keep the line short; set it\n // back to \"turn\" (or anything else) through `labels.turn`.\n turn: \"\",\n session: \"sess\",\n step: \"step\",\n steps: \"steps\",\n input: \"in\",\n cached: \"cached\",\n output: \"out\",\n reasoning: \"reason\",\n cost: \"cost\",\n cacheExpired: \"cache expired\",\n providerChanged: \"changed\",\n modelChanged: \"model changed\",\n}\n\nexport const DEFAULT_FIELDS: LineFields = {\n clock: true,\n delta: true,\n model: true,\n variant: true,\n turn: true,\n session: true,\n reasoning: false,\n // The turn's and the session's cost are on by default: what a session costs\n // is the question the tokens only half answer. A zero is left out, so a free\n // model reads as free instead of repeating `$0` on every line.\n cost: true,\n cacheExpiry: true,\n}\n\nexport function formatClock(ms: number): string {\n const date = new Date(ms)\n const pad = (value: number) => String(value).padStart(2, \"0\")\n return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n}\n\nexport function formatDuration(ms: number): string {\n const totalSeconds = Math.max(0, Math.floor(ms / 1000))\n const seconds = totalSeconds % 60\n const minutes = Math.floor(totalSeconds / 60) % 60\n const hours = Math.floor(totalSeconds / 3600) % 24\n const days = Math.floor(totalSeconds / 86400)\n if (days > 0) return `${days}d${hours}h`\n if (hours > 0) return `${hours}h${String(minutes).padStart(2, \"0\")}m`\n if (minutes > 0) return `${minutes}m${String(seconds).padStart(2, \"0\")}s`\n return `${seconds}s`\n}\n\nexport function formatTokens(value: number): string {\n const n = Math.max(0, Math.round(value))\n if (n < 1_000) return String(n)\n if (n < 999_500) {\n const k = n / 1_000\n return `${trimZeros(k.toFixed(k < 10 ? 2 : 1))}k`\n }\n if (n < 999_500_000) {\n const m = n / 1_000_000\n return `${trimZeros(m.toFixed(m < 10 ? 2 : 1))}M`\n }\n return `${Math.round(n / 1_000_000)}M`\n}\n\nfunction trimZeros(value: string): string {\n return value.includes(\".\") ? value.replace(/0+$/, \"\").replace(/\\.$/, \"\") : value\n}\n\nexport function formatCost(value: number): string {\n if (value <= 0) return \"$0\"\n if (value < 1) return `$${value.toFixed(4)}`\n return `$${value.toFixed(2)}`\n}\n\n/**\n * The buckets the line displays, derived from a totals group. `input` is the\n * *uncached* part of the prompt (fresh input plus cache writes, i.e. what the\n * debug line's `new` counts on the prompt side), `prompt` is the whole prompt\n * of the request and `output` is everything the model generated (visible output\n * + reasoning). OpenCode's own debug line reconciles with these:\n * `new = in + out` and `total = prompt + out`, with `prompt = in + cached`.\n * The stored buckets stay raw; only the display folds.\n */\nexport type DisplayTotals = Totals & {\n /** Whole prompt of the request: fresh input + cached reads + cache writes. */\n prompt: number\n}\n\nexport function displayTotals(totals: Totals): DisplayTotals {\n const prompt = totals.input + totals.cached + totals.cacheWrite\n return {\n input: prompt - totals.cached,\n cached: totals.cached,\n cacheWrite: totals.cacheWrite,\n output: totals.output + totals.reasoning,\n reasoning: totals.reasoning,\n cost: totals.cost,\n steps: totals.steps,\n prompt,\n }\n}\n\n/**\n * The numbers of one totals group. `cost` is passed explicitly because the two\n * callers mean different money: a turn's cost is the sum over its steps\n * whatever view its tokens follow, and money never comes from a \"latest\" step.\n * A zero cost is left out of the line.\n */\nfunction totalsText(prefix: string, totals: Totals, labels: LineLabels, fields: LineFields, stepCount = 0, cost = totals.cost): string {\n const display = displayTotals(totals)\n // The parenthesised value is the whole prompt, shown only when it differs\n // from the uncached part the label leads with.\n const input =\n display.prompt === display.input\n ? `${labels.input} ${formatTokens(display.input)}`\n : `${labels.input} ${formatTokens(display.input)} (${formatTokens(display.prompt)})`\n const parts = [\n input,\n `${labels.cached} ${formatTokens(display.cached)}`,\n `${labels.output} ${formatTokens(display.output)}`,\n ]\n if (fields.reasoning) parts.push(`${labels.reasoning} ${formatTokens(display.reasoning)}`)\n if (fields.cost && cost > 0) parts.push(`${labels.cost} ${formatCost(cost)}`)\n // An empty label (the default for the turn) leaves the numbers unlabelled; a\n // multi-step turn still says how many requests it took.\n const steps = stepCount > 1 ? `${stepCount} ${stepCount === 1 ? labels.step : labels.steps}` : \"\"\n const head = [prefix, steps].filter((piece) => piece !== \"\").join(\" \")\n const numbers = parts.join(\", \")\n if (head === \"\") return numbers\n return stepCount > 1 ? `${head} · ${numbers}` : `${head} ${numbers}`\n}\n\n/**\n * Render the telemetry line as toned segments. The caller maps tones onto\n * theme colors; `formatLine` joins the segments as plain text.\n */\nexport function formatLineSegments(\n stats: TurnStats,\n options?: { labels?: Partial<LineLabels>; fields?: Partial<LineFields>; turnTotals?: TurnTotalsView },\n): LineSegment[] {\n const labels = { ...DEFAULT_LABELS, ...options?.labels }\n const fields = { ...DEFAULT_FIELDS, ...options?.fields }\n const turnTotals = options?.turnTotals ?? \"latest\"\n const segments: LineSegment[] = []\n\n if (fields.clock && stats.endAt !== undefined) {\n segments.push({ text: formatClock(stats.endAt), tone: \"muted\" })\n }\n if (fields.delta && stats.promptGapMs !== undefined) {\n segments.push({ text: `${labels.delta} ${formatDuration(stats.promptGapMs)}`, tone: \"muted\" })\n }\n // The turn's identity is the provider the model ran through and the model\n // itself: the same model can come from different providers, so both are\n // shown, and a provider switch is the change the line warns about.\n if (stats.providerID !== undefined) {\n let text = stats.providerID\n if (fields.model && stats.modelID !== undefined) text += `/${stats.modelID}`\n if (fields.variant && stats.variant) text += ` ${stats.variant}`\n segments.push({ text, tone: stats.providerChanged ? \"warning\" : \"base\" })\n if (stats.providerChanged) segments.push({ text: labels.providerChanged, tone: \"warning\" })\n else if (stats.modelChanged) segments.push({ text: labels.modelChanged, tone: \"muted\" })\n }\n if (fields.turn && stats.totals.steps > 0) {\n const totals = turnTotals === \"sum\" ? stats.totals : stats.latest\n // The tokens follow the view — `latest` is the context the turn ended with\n // — but money is paid per step: the turn's cost is the sum over its steps,\n // never the last step's share of it.\n segments.push({\n text: totalsText(labels.turn, totals, labels, fields, turnTotals === \"sum\" ? stats.totals.steps : stats.steps, stats.totals.cost),\n tone: \"base\",\n })\n }\n if (fields.session && stats.session) {\n segments.push({ text: totalsText(labels.session, stats.session, labels, fields, 0, stats.session.cost), tone: \"muted\" })\n }\n if (fields.cacheExpiry && stats.cacheExpired) {\n const count = stats.drops.length > 1 ? `×${stats.drops.length}` : \"\"\n segments.push({ text: `⚠ ${labels.cacheExpired} (−${formatTokens(stats.cacheDropTokens)}${count})`, tone: \"warning\" })\n }\n return segments\n}\n\nexport function formatLine(\n stats: TurnStats,\n options?: { labels?: Partial<LineLabels>; fields?: Partial<LineFields>; turnTotals?: TurnTotalsView },\n): string {\n return formatLineSegments(stats, options)\n .map((segment) => segment.text)\n .join(\" · \")\n}\n",
7
+ "/**\n * Text hygiene for everything that reaches a terminal.\n *\n * Some of what the plugin displays is not written by the user: a model writes\n * a session title, an NDJSON export can come from anywhere. A control\n * character in any of those is interpreted by the terminal that renders it\n * (title changes, cursor moves, colours), so every such string is stripped of\n * control characters before it is stored or drawn.\n *\n * The rule is deliberately blunt: drop C0 controls, DEL and the C1 block, keep\n * everything else (the text is otherwise arbitrary Unicode), and cap the\n * length so a hostile value cannot bloat the database or the frame. Layout is\n * never touched: whitespace is preserved, because callers build indentation\n * into the strings they truncate.\n */\n\n/** Longest value kept by default: a title, an id or a path, not a document. */\nexport const TEXT_LIMIT = 512\n\n/**\n * Strip control characters and cap the length. Newlines and tabs go too: a\n * line is one line, and the renderers lay out their own spacing.\n */\nexport function sanitizeText(value: string, limit = TEXT_LIMIT): string {\n if (limit <= 0) return \"\"\n let cleaned = \"\"\n for (const character of value) {\n const code = character.codePointAt(0) ?? 0\n if (code <= 0x1f || code === 0x7f || (code >= 0x80 && code <= 0x9f)) continue\n cleaned += character\n if (cleaned.length >= limit) break\n }\n return cleaned\n}\n\n/** `sanitizeText` without the layout, for a stored label: trimmed as well. */\nexport function sanitizeLabel(value: string, limit = TEXT_LIMIT): string {\n return sanitizeText(value, limit).trim()\n}\n\n/**\n * A single identifier token: the shape a provider id or a model id has. Used\n * to validate the values that become grouping keys in the database, so a\n * configured provider cannot inject anything into an identity a view groups\n * by. Case is left alone: the provider is shown as it is configured, and\n * folding it would report a provider the user never chose.\n */\nconst TOKEN = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/\n\nexport function isToken(value: string, limit = 64): boolean {\n return value.length > 0 && value.length <= limit && TOKEN.test(value)\n}\n",
8
+ "import {\n formatLineSegments,\n type LineFields,\n type LineLabels,\n type LineTone,\n type Totals,\n type TranscriptMessage,\n type TurnStats,\n type TurnTotalsView,\n} from \"./core\"\nimport { PROMPT_BOUNDARIES } from \"./core\"\nimport { sanitizeLabel } from \"./text\"\n\n/**\n * Adapter between the host's session messages and the pure turn accounting in\n * `core`, plus the plain-text parts the TUI line is made of.\n *\n * The host types are shadowed structurally: the plugin only reads fields it\n * relies on, so a host refactor degrades the line instead of breaking setup.\n */\n\nexport type RuntimeModelRef = {\n providerID?: string | undefined\n id?: string | undefined\n variant?: string | undefined\n}\n\nexport type RuntimeTokenUsage = {\n input?: number | undefined\n output?: number | undefined\n reasoning?: number | undefined\n cache?: { read?: number | undefined; write?: number | undefined } | undefined\n}\n\nexport type RuntimeMessage = {\n id?: string | undefined\n type?: string | undefined\n time?: { created?: number | undefined; streamed?: number | undefined; completed?: number | undefined } | undefined\n model?: RuntimeModelRef | undefined\n tokens?: RuntimeTokenUsage | undefined\n cost?: number | undefined\n finish?: string | null | undefined\n rawFinish?: string | null | undefined\n}\n\nexport type RuntimeSessionInfo = {\n cost?: number | undefined\n tokens?: RuntimeTokenUsage | undefined\n time?: { created?: number | undefined } | undefined\n}\n\nexport type LinePart = {\n text: string\n tone: LineTone\n}\n\n/** Convert host messages into the structural transcript `core` understands. */\nexport function toTranscript(messages: readonly RuntimeMessage[]): TranscriptMessage[] {\n const transcript: TranscriptMessage[] = []\n for (const message of messages) {\n if (typeof message.id !== \"string\" || typeof message.type !== \"string\") continue\n if (message.type !== \"assistant\") {\n transcript.push({ type: message.type, id: message.id, time: message.time })\n continue\n }\n transcript.push({\n type: \"assistant\",\n id: message.id,\n time: message.time,\n model: toModelRef(message.model),\n tokens: message.tokens,\n cost: message.cost,\n finish: message.finish ?? undefined,\n rawFinish: message.rawFinish ?? undefined,\n })\n }\n return transcript\n}\n\n/**\n * Convert only the messages of the last `segments` turns.\n *\n * The line is computed from the tail of a transcript (`computeTurn` walks it\n * backwards), so converting the whole session was pure waste: on a long session\n * it cost milliseconds at every turn end, on the thread that also paints the\n * agent's output. Walking backwards costs what the last turns hold.\n */\nexport function toTranscriptTail(messages: readonly RuntimeMessage[], segments = 2): TranscriptMessage[] {\n // `computeTurn` reads the last turn *and the one before it*: with fewer\n // segments it answers something else (no `promptGapMs`, no cache reference for\n // the first step), so the floor is not a matter of taste.\n const wanted = Math.max(2, segments)\n const tail: RuntimeMessage[] = []\n let collected = 0\n let closed = 0\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]!\n if (message.type === \"assistant\") {\n tail.push(message)\n collected += 1\n continue\n }\n if (message.type === undefined || !PROMPT_BOUNDARIES.has(message.type)) continue\n // A boundary closes the run collected after it, exactly like the host-side\n // walk `transcriptTail` does.\n if (collected === 0) continue\n tail.push(message)\n collected = 0\n closed += 1\n if (closed >= wanted) break\n }\n tail.reverse()\n return toTranscript(tail)\n}\n\nfunction toModelRef(model: RuntimeModelRef | undefined): { providerID: string; id: string; variant?: string } | undefined {\n if (model === undefined || model.providerID === undefined || model.id === undefined) return undefined\n if (model.variant === undefined) return { providerID: model.providerID, id: model.id }\n return { providerID: model.providerID, id: model.id, variant: model.variant }\n}\n\n/** Session-wide accumulated usage, as reported by the host on the session. */\nexport function sessionTotals(info: RuntimeSessionInfo | undefined): Totals | undefined {\n if (info === undefined) return undefined\n const tokens = info.tokens\n return {\n input: tokens?.input ?? 0,\n cached: tokens?.cache?.read ?? 0,\n output: tokens?.output ?? 0,\n reasoning: tokens?.reasoning ?? 0,\n cacheWrite: tokens?.cache?.write ?? 0,\n cost: info.cost ?? 0,\n steps: 0,\n }\n}\n\n/**\n * The telemetry line as renderable parts: each segment plus the muted\n * separators between them, so the TUI can color every part independently.\n */\nexport function lineParts(\n stats: TurnStats | undefined,\n options?: { labels?: Partial<LineLabels>; fields?: Partial<LineFields>; turnTotals?: TurnTotalsView },\n): LinePart[] {\n if (stats === undefined) return []\n const parts: LinePart[] = []\n const segments = formatLineSegments(stats, options)\n segments.forEach((segment, index) => {\n if (index > 0) parts.push({ text: \" · \", tone: \"muted\" })\n // The line leads with identities that came from outside (the provider and\n // the model): one text renderable, one terminal, so no control character\n // may reach it.\n parts.push({ text: sanitizeLabel(segment.text), tone: segment.tone })\n })\n return parts\n}\n\n/** Plain text of the same line, useful for tests and logs. */\nexport function lineText(parts: readonly LinePart[]): string {\n return parts.map((part) => part.text).join(\"\")\n}\n",
9
+ "import type { Database } from \"bun:sqlite\"\nimport { cacheDrop, stepGap, tokensPerSecond, type AssistantMessage } from \"../core\"\n\n/**\n * Read side of the stats engine: every function takes an open database and\n * returns plain rows for the TUI panel, the report script and the NDJSON\n * exports. Aggregations stay in SQL; comparisons and percentiles that would\n * need window functions or custom SQLite builds are computed in JS.\n */\n\n/** Event kind written by the recorder when a compaction completes. */\nexport const COMPACTION_KIND = \"compaction\"\n\n/**\n * Idle gap above which a cache reuse loss is attributed to provider-side\n * expiry rather than to a mid-session event. Heuristic: the empirical TTL\n * evidence view narrows this down with real observations.\n */\nexport const DEFAULT_TTL_HINT_MS = 10 * 60 * 1000\n\nexport type Range = {\n from?: number | undefined\n to?: number | undefined\n}\n\n/**\n * Panel filters. They narrow every aggregation to one model, one provider or\n * one session, and are applied with the same `WHERE` builder the range uses so\n * a filtered view and a drill-down agree.\n */\nexport type Filters = {\n /** Model id, variant excluded. */\n modelID?: string | undefined\n /**\n * One model *and* its variant, as `modelKey` builds it (`m1#max`). A variant\n * is a different model to the provider, so a drill-down into one variant must\n * not pick up its family.\n */\n modelKey?: string | undefined\n /**\n * The provider the model ran through, as OpenCode configured it\n * (`opencode-go`, `deepseek`, `deepinfra`). It is the identity every view\n * groups by: a gateway may re-route the request, but the upstream that\n * actually ran it is not observable.\n */\n provider?: string | undefined\n /**\n * One session, exactly. Used by the `session` range and by the overview's\n * \"this session\" block, which must not pick up the steps of every other\n * session that happened to run after it started.\n */\n sessionID?: string | undefined\n}\n\nexport type Usage = {\n steps: number\n input: number\n cached: number\n cacheWrite: number\n output: number\n reasoning: number\n cost: number\n}\n\ntype UsageRow = {\n steps: number | null\n input: number | null\n cached: number | null\n cacheWrite: number | null\n output: number | null\n reasoning: number | null\n cost: number | null\n}\n\nexport const STEP_TIME = \"COALESCE(created_at, recorded_at)\"\n\n/**\n * The identity of a model *and* its variant, in SQL: `modelKey` says a variant\n * is a different model to the provider, so a variant never shares a row with\n * its family. The JS twin is `modelKey` below; both have to agree.\n */\nexport const MODEL_KEY_SQL = \"COALESCE(model_id, '(unknown)') || '#' || COALESCE(variant, '')\"\n\n/** `modelKey(modelID, variant)`: the family, with the variant as a suffix. */\nexport function modelKey(modelID: string, variant: string | undefined): string {\n return `${modelID}#${variant ?? \"\"}`\n}\n\n/** Human label of a model key or a model id and variant. */\nexport function modelLabel(modelID: string, variant: string | undefined): string {\n return variant === undefined ? modelID : `${modelID} ${variant}`\n}\nconst USAGE_SELECT = `COUNT(*) AS steps,\n COALESCE(SUM(input_tokens), 0) AS input,\n COALESCE(SUM(cache_read), 0) AS cached,\n COALESCE(SUM(cache_write), 0) AS cacheWrite,\n COALESCE(SUM(output_tokens), 0) AS output,\n COALESCE(SUM(reasoning_tokens), 0) AS reasoning,\n COALESCE(SUM(cost), 0) AS cost`\n\nfunction usageFrom(row: UsageRow): Usage {\n return {\n steps: row.steps ?? 0,\n input: row.input ?? 0,\n cached: row.cached ?? 0,\n cacheWrite: row.cacheWrite ?? 0,\n output: row.output ?? 0,\n reasoning: row.reasoning ?? 0,\n cost: row.cost ?? 0,\n }\n}\n\nfunction rangeWhere(range: Range | undefined, column = STEP_TIME): { where: string; params: number[] } {\n const clauses: string[] = []\n const params: number[] = []\n if (range?.from !== undefined) {\n clauses.push(`${column} >= ?`)\n params.push(range.from)\n }\n if (range?.to !== undefined) {\n clauses.push(`${column} < ?`)\n params.push(range.to)\n }\n return { where: clauses.length > 0 ? `WHERE ${clauses.join(\" AND \")}` : \"\", params }\n}\n\n/**\n * `WHERE` for a range plus the panel filters. Shared with the analysis layer so\n * a filtered view and a drill-down agree on what is in scope.\n */\nexport function buildWhere(\n range: Range | undefined,\n filters?: Filters | undefined,\n column = STEP_TIME,\n): { where: string; params: (string | number)[] } {\n const rangeClause = rangeWhere(range, column)\n const clauses = rangeClause.where === \"\" ? [] : [rangeClause.where.slice(\"WHERE \".length)]\n const params: (string | number)[] = [...rangeClause.params]\n if (filters?.modelID !== undefined && filters.modelID !== \"\") {\n clauses.push(\"model_id = ?\")\n params.push(filters.modelID)\n }\n if (filters?.modelKey !== undefined && filters.modelKey !== \"\") {\n clauses.push(`${MODEL_KEY_SQL} = ?`)\n params.push(filters.modelKey)\n }\n if (filters?.provider !== undefined && filters.provider !== \"\") {\n clauses.push(\"provider_id = ?\")\n params.push(filters.provider)\n }\n if (filters?.sessionID !== undefined && filters.sessionID !== \"\") {\n clauses.push(\"session_id = ?\")\n params.push(filters.sessionID)\n }\n return { where: clauses.length > 0 ? `WHERE ${clauses.join(\" AND \")}` : \"\", params }\n}\n\n/**\n * Share of the whole prompt that came from the cache. The denominator is what\n * the provider saw: cache reads, fresh input and cache writes.\n */\nexport function cacheHitRatio(usage: Pick<Usage, \"input\" | \"cached\" | \"cacheWrite\">): number | undefined {\n const total = usage.cached + usage.input + usage.cacheWrite\n return total > 0 ? usage.cached / total : undefined\n}\n\nexport type Overview = Usage & {\n sessions: number\n prompts: number\n firstAt: number | undefined\n lastAt: number | undefined\n cacheHitRatio: number | undefined\n /**\n * Tokens generated per second of streaming, over the whole selection: the\n * produced tokens divided by the summed streaming windows. A weighted average,\n * not the mean of the per-step rates the latency view reports as p50.\n */\n tps: number | undefined\n}\n\nexport function overview(db: Database, range?: Range, filters?: Filters): Overview {\n const steps = buildWhere(range, filters)\n const row = db\n .query(\n `SELECT ${USAGE_SELECT},\n COUNT(DISTINCT session_id) AS sessions,\n MIN(${STEP_TIME}) AS firstAt,\n MAX(COALESCE(completed_at, ${STEP_TIME})) AS lastAt,\n COALESCE(SUM(CASE WHEN streamed_at > created_at THEN output_tokens + reasoning_tokens ELSE 0 END), 0) AS produced,\n COALESCE(SUM(CASE WHEN streamed_at > created_at THEN streamed_at - created_at ELSE 0 END), 0) AS window\n FROM steps ${steps.where}`,\n )\n .get(...steps.params) as\n | (UsageRow & { sessions: number | null; firstAt: number | null; lastAt: number | null; produced: number | null; window: number | null })\n | null\n\n const prompts = rangeWhere(range, \"created_at\")\n const promptRow = db.query(`SELECT COUNT(*) AS prompts FROM prompts ${prompts.where}`).get(...prompts.params) as { prompts: number } | null\n\n const usage = usageFrom(row ?? { steps: 0, input: 0, cached: 0, cacheWrite: 0, output: 0, reasoning: 0, cost: 0 })\n return {\n ...usage,\n sessions: row?.sessions ?? 0,\n prompts: promptRow?.prompts ?? 0,\n firstAt: row?.firstAt ?? undefined,\n lastAt: row?.lastAt ?? undefined,\n cacheHitRatio: cacheHitRatio(usage),\n tps: row === null ? undefined : tokensPerSecond(row.produced ?? 0, 0, row.window ?? 0),\n }\n}\n\nexport type ProviderUsage = Usage & {\n providerID: string\n firstAt: number | undefined\n lastAt: number | undefined\n}\n\nexport type ModelUsage = {\n modelID: string\n variant: string | undefined\n providers: ProviderUsage[]\n totals: Usage\n}\n\ntype ModelRow = UsageRow & {\n provider_id: string | null\n model_id: string | null\n variant: string | null\n first_at: number | null\n last_at: number | null\n}\n\n/**\n * Usage per model, each with the providers that served it. This is the view\n * that answers \"through which provider is this model actually coming, over the\n * selected period\".\n */\nexport function models(db: Database, range?: Range, filters?: Filters): ModelUsage[] {\n const filter = buildWhere(range, filters)\n const rows = db\n .query(\n `SELECT provider_id, model_id, variant,\n ${USAGE_SELECT},\n MIN(${STEP_TIME}) AS first_at,\n MAX(COALESCE(completed_at, recorded_at)) AS last_at\n FROM steps ${filter.where}\n GROUP BY provider_id, model_id, variant\n ORDER BY steps DESC, cached DESC`,\n )\n .all(...filter.params) as ModelRow[]\n\n const models = new Map<string, ModelUsage>()\n for (const row of rows) {\n const modelID = row.model_id ?? \"(unknown)\"\n const variant = row.variant ?? undefined\n const key = `${modelID}#${variant ?? \"\"}`\n let model = models.get(key)\n if (!model) {\n model = { modelID, variant, providers: [], totals: emptyUsage() }\n models.set(key, model)\n }\n const usage = usageFrom(row)\n model.providers.push({\n ...usage,\n providerID: row.provider_id ?? \"(unknown)\",\n firstAt: row.first_at ?? undefined,\n lastAt: row.last_at ?? undefined,\n })\n addUsage(model.totals, usage)\n }\n return [...models.values()].sort((a, b) => billedTokens(b.totals) - billedTokens(a.totals))\n}\n\nfunction emptyUsage(): Usage {\n return { steps: 0, input: 0, cached: 0, cacheWrite: 0, output: 0, reasoning: 0, cost: 0 }\n}\n\nfunction addUsage(target: Usage, source: Usage): void {\n target.steps += source.steps\n target.input += source.input\n target.cached += source.cached\n target.cacheWrite += source.cacheWrite\n target.output += source.output\n target.reasoning += source.reasoning\n target.cost += source.cost\n}\n\nfunction billedTokens(usage: Pick<Usage, \"input\" | \"cached\" | \"output\">): number {\n return usage.input + usage.cached + usage.output\n}\n\nexport type TimelineBucket = { at: number } & Usage\n\n/** Usage aggregated into fixed time buckets (for example one hour or day). */\nexport function timeline(db: Database, range: Range | undefined, bucketMs: number): TimelineBucket[] {\n const filter = rangeWhere(range)\n const rows = db\n .query(\n `SELECT CAST(${STEP_TIME} / ? AS INTEGER) * ? AS bucket, ${USAGE_SELECT}\n FROM steps ${filter.where}\n GROUP BY bucket\n ORDER BY bucket`,\n )\n .all(bucketMs, bucketMs, ...filter.params) as (UsageRow & { bucket: number })[]\n return rows.map((row) => ({ at: row.bucket, ...usageFrom(row) }))\n}\n\nexport type DropReason = \"compaction\" | \"expired\" | \"unknown\"\n\nexport type CacheDropRow = {\n sessionID: string\n messageID: string\n providerID: string | undefined\n modelID: string | undefined\n variant: string | undefined\n at: number\n tokens: number\n gapMs: number | undefined\n prevCached: number\n reason: DropReason\n}\n\nexport type TtlEvidenceRow = {\n providerID: string\n modelID: string\n samples: number\n /** Largest idle gap that still hit the cache. */\n maxHitGapMs: number | undefined\n /** Smallest idle gap after which reuse was lost. */\n minDropGapMs: number | undefined\n}\n\nexport type LatencyStats = {\n count: number\n p50: number | undefined\n p90: number | undefined\n p99: number | undefined\n}\n\nexport type LatencyReport = {\n /** Milliseconds from request to completed response. */\n wall: LatencyStats\n /** Milliseconds from request to the last streamed chunk (the streaming window). */\n stream: LatencyStats\n /** Output-plus-reasoning tokens per second over that window. */\n tps: LatencyStats\n}\n\n/** Nearest-rank percentile over an unsorted sample. */\nexport function percentile(values: readonly number[], p: number): number | undefined {\n if (values.length === 0) return undefined\n const sorted = [...values].sort((a, b) => a - b)\n const rank = Math.ceil((p / 100) * sorted.length)\n const index = Math.min(sorted.length - 1, Math.max(0, rank - 1))\n return sorted[index]\n}\n\nexport function latencyStats(values: number[]): LatencyStats {\n return {\n count: values.length,\n p50: percentile(values, 50),\n p90: percentile(values, 90),\n p99: percentile(values, 99),\n }\n}\n\nconst statsFor = latencyStats\n\nexport type SessionUsage = Usage & {\n sessionID: string\n parentID: string | undefined\n title: string | undefined\n directory: string | undefined\n providers: string[]\n models: string[]\n prompts: number\n firstAt: number | undefined\n lastAt: number | undefined\n cacheHitRatio: number | undefined\n}\n\nexport function sessions(\n db: Database,\n options?: { range?: Range; filters?: Filters; limit?: number; sessionIDs?: readonly string[] },\n): SessionUsage[] {\n if (options?.sessionIDs !== undefined && options.sessionIDs.length === 0) return []\n const range = options?.range\n const filter = buildWhere(range, options?.filters, \"COALESCE(st.created_at, st.recorded_at)\")\n const limit = options?.limit ?? 20\n const clauses = filter.where === \"\" ? [] : [filter.where.slice(\"WHERE \".length)]\n const params: (number | string)[] = [...filter.params]\n if (options?.sessionIDs !== undefined) {\n // A specific set of sessions (the subagents of one session): the group by\n // then reads one index range per session instead of every step recorded.\n const ids = options.sessionIDs.slice(0, 500)\n clauses.push(`st.session_id IN (${ids.map(() => \"?\").join(\", \")})`)\n params.push(...ids)\n }\n const rows = db\n .query(\n `SELECT st.session_id, s.title, s.directory, s.parent_id,\n GROUP_CONCAT(DISTINCT st.provider_id) AS providers,\n GROUP_CONCAT(DISTINCT st.model_id) AS models,\n (SELECT COUNT(*) FROM prompts p WHERE p.session_id = st.session_id) AS prompts,\n COUNT(*) AS steps,\n COALESCE(SUM(st.input_tokens), 0) AS input,\n COALESCE(SUM(st.cache_read), 0) AS cached,\n COALESCE(SUM(st.cache_write), 0) AS cacheWrite,\n COALESCE(SUM(st.output_tokens), 0) AS output,\n COALESCE(SUM(st.reasoning_tokens), 0) AS reasoning,\n COALESCE(SUM(st.cost), 0) AS cost,\n MIN(COALESCE(st.created_at, st.recorded_at)) AS first_at,\n MAX(COALESCE(st.completed_at, st.created_at, st.recorded_at)) AS last_at\n FROM steps st\n LEFT JOIN sessions s ON s.session_id = st.session_id\n ${clauses.length > 0 ? `WHERE ${clauses.join(\" AND \")}` : \"\"}\n GROUP BY st.session_id\n ORDER BY last_at DESC\n LIMIT ?`,\n )\n .all(\n ...params,\n limit,\n ) as (UsageRow & {\n session_id: string\n title: string | null\n directory: string | null\n parent_id: string | null\n providers: string | null\n models: string | null\n prompts: number | null\n first_at: number | null\n last_at: number | null\n })[]\n\n return rows.map((row) => {\n const usage = usageFrom(row)\n return {\n ...usage,\n sessionID: row.session_id,\n parentID: row.parent_id ?? undefined,\n title: row.title ?? undefined,\n directory: row.directory ?? undefined,\n providers: row.providers ? row.providers.split(\",\").filter(Boolean) : [],\n models: row.models ? row.models.split(\",\").filter(Boolean) : [],\n prompts: row.prompts ?? 0,\n firstAt: row.first_at ?? undefined,\n lastAt: row.last_at ?? undefined,\n cacheHitRatio: cacheHitRatio(usage),\n }\n })\n}\n\n/** Sessions whose parent is `parentID`: the subagents of one session. */\nexport function childSessionIDs(db: Database, parentID: string): string[] {\n return (db.query(\"SELECT session_id FROM sessions WHERE parent_id = ? ORDER BY session_id\").all(parentID) as { session_id: string }[]).map(\n (row) => row.session_id,\n )\n}\n\n/** Session usage rows for a specific set of sessions (subagent rollups). */\nexport function sessionUsageOf(db: Database, sessionIDs: readonly string[]): SessionUsage[] {\n return sessions(db, { sessionIDs, limit: sessionIDs.length })\n}\n\nexport type TurnDrop = {\n messageID: string\n tokens: number\n gapMs: number | undefined\n}\n\n/**\n * One finished turn of a session, built from the recorded steps and prompts. It\n * carries the same buckets the telemetry line uses, so the panel can render the\n * per-prompt history with the line's own formatter.\n */\nexport type TurnUsage = {\n sessionID: string\n /** Creation time of the prompt that opened the turn, when it was recorded. */\n promptAt: number | undefined\n endAt: number | undefined\n /** Time between the previous prompt of the session and this turn's prompt. */\n promptGapMs: number | undefined\n providerID: string | undefined\n modelID: string | undefined\n variant: string | undefined\n providerChanged: boolean\n modelChanged: boolean\n steps: number\n /** Raw buckets of the last step: the prompt the turn ended with. */\n latest: Usage\n /** Raw buckets summed over every step of the turn. */\n totals: Usage\n drops: TurnDrop[]\n /** Finish reason of the turn's last step. */\n finish: string | undefined\n /**\n * Tokens generated per second of streaming over the turn's steps: produced\n * tokens divided by their summed streaming windows. For a one-step turn it is\n * that step's rate.\n */\n tps: number | undefined\n}\n\ntype TurnStepRow = {\n message_id: string\n session_id: string\n provider_id: string | null\n model_id: string | null\n variant: string | null\n input_tokens: number | null\n cache_read: number | null\n cache_write: number | null\n output_tokens: number | null\n reasoning_tokens: number | null\n cost: number | null\n created_at: number | null\n streamed_at: number | null\n recorded_at: number\n completed_at: number | null\n finish: string | null\n}\n\n/** A turn is finished when its last step ended without asking for more tools. */\nfunction turnFinished(finish: string | null): boolean {\n return finish !== null && finish !== \"\" && finish !== \"tool-calls\"\n}\n\nfunction stepUsage(row: TurnStepRow): Usage {\n return {\n steps: 1,\n input: row.input_tokens ?? 0,\n cached: row.cache_read ?? 0,\n cacheWrite: row.cache_write ?? 0,\n output: row.output_tokens ?? 0,\n reasoning: row.reasoning_tokens ?? 0,\n cost: row.cost ?? 0,\n }\n}\n\n/** Structural view of a recorded step, so the shared cache-drop rule applies. */\nfunction stepMessage(row: TurnStepRow): AssistantMessage {\n return {\n type: \"assistant\",\n id: row.message_id,\n time: { created: row.created_at ?? row.recorded_at, completed: row.completed_at ?? undefined },\n model:\n row.provider_id !== null && row.model_id !== null\n ? { providerID: row.provider_id, id: row.model_id, variant: row.variant ?? undefined }\n : undefined,\n tokens: {\n input: row.input_tokens ?? 0,\n output: row.output_tokens ?? 0,\n reasoning: row.reasoning_tokens ?? 0,\n cache: { read: row.cache_read ?? 0, write: row.cache_write ?? 0 },\n },\n finish: row.finish ?? undefined,\n }\n}\n\n/**\n * Finished turns, newest first. Steps belong to the newest prompt recorded at or\n * before them; a turn whose last step asked for tools is dropped, because it is\n * still running — the same rule the telemetry line follows.\n *\n * For one session the read is bounded by a window: only the steps at or after\n * the `limit`-th newest prompt are loaded, so a session with tens of thousands\n * of steps costs the same as a short one. The extra prompt is kept as the\n * reference the oldest returned turn measures its gap against.\n */\nexport function turns(\n db: Database,\n options?: { sessionID?: string | undefined; range?: Range | undefined; filters?: Filters | undefined; limit?: number | undefined },\n): TurnUsage[] {\n const limit = options?.limit ?? 50\n const filter = buildWhere(options?.range, options?.filters)\n const clauses = filter.where === \"\" ? [] : [filter.where.slice(\"WHERE \".length)]\n const params: (number | string)[] = [...filter.params]\n if (options?.sessionID !== undefined) {\n clauses.push(\"session_id = ?\")\n params.push(options.sessionID)\n }\n const floor = options?.sessionID === undefined ? undefined : promptFloor(db, options.sessionID, limit + 1)\n if (floor !== undefined) {\n clauses.push(`${STEP_TIME} >= ?`)\n params.push(floor)\n }\n const steps = db\n .query(\n `SELECT message_id, session_id, provider_id, model_id, variant,\n input_tokens, cache_read, cache_write, output_tokens, reasoning_tokens, cost,\n created_at, streamed_at, recorded_at, completed_at, finish\n FROM steps ${clauses.length > 0 ? `WHERE ${clauses.join(\" AND \")}` : \"\"}\n ORDER BY session_id, ${STEP_TIME}`,\n )\n .all(...params) as TurnStepRow[]\n\n const promptClause = options?.sessionID === undefined ? \"\" : `WHERE session_id = ?${floor === undefined ? \"\" : \" AND created_at >= ?\"}`\n const promptParams = options?.sessionID === undefined ? [] : floor === undefined ? [options.sessionID] : [options.sessionID, floor]\n const promptRows = db\n .query(`SELECT session_id, created_at FROM prompts ${promptClause} ORDER BY session_id, created_at`)\n .all(...promptParams) as { session_id: string; created_at: number | null }[]\n\n const promptsBySession = new Map<string, number[]>()\n for (const row of promptRows) {\n if (row.created_at === null) continue\n const list = promptsBySession.get(row.session_id) ?? []\n list.push(row.created_at)\n promptsBySession.set(row.session_id, list)\n }\n\n const result: TurnUsage[] = []\n let sessionID: string | undefined\n let bucket: TurnStepRow[] = []\n let promptIndex = -2\n let promptAt: number | undefined\n let previousPromptAt: number | undefined\n\n function close(): void {\n const last = bucket[bucket.length - 1]\n if (last === undefined || !turnFinished(last.finish)) {\n bucket = []\n return\n }\n const totals = emptyUsage()\n for (const row of bucket) addUsage(totals, stepUsage(row))\n const drops: TurnDrop[] = []\n let reference: AssistantMessage | undefined\n for (const row of bucket) {\n const message = stepMessage(row)\n const drop = cacheDrop(reference, message)\n if (drop !== undefined) {\n drops.push({ messageID: row.message_id, tokens: drop, gapMs: stepGap(reference, message) })\n }\n reference = message\n }\n const providerID = last.provider_id ?? undefined\n const modelID = last.model_id ?? undefined\n const variant = last.variant ?? undefined\n let produced = 0\n let window = 0\n for (const row of bucket) {\n if (row.created_at === null || row.streamed_at === null || row.streamed_at <= row.created_at) continue\n produced += (row.output_tokens ?? 0) + (row.reasoning_tokens ?? 0)\n window += row.streamed_at - row.created_at\n }\n const previous = result.length > 0 && result[result.length - 1]!.sessionID === last.session_id ? result[result.length - 1] : undefined\n result.push({\n sessionID: last.session_id,\n promptAt,\n endAt: last.completed_at ?? last.created_at ?? last.recorded_at,\n promptGapMs: promptAt !== undefined && previousPromptAt !== undefined ? Math.max(0, promptAt - previousPromptAt) : undefined,\n providerID,\n modelID,\n variant,\n providerChanged: previous?.providerID !== undefined && providerID !== undefined && previous.providerID !== providerID,\n modelChanged:\n previous !== undefined &&\n previous.providerID === providerID &&\n `${previous.modelID ?? \"\"}#${previous.variant ?? \"\"}` !== `${modelID ?? \"\"}#${variant ?? \"\"}`,\n steps: bucket.length,\n latest: stepUsage(last),\n totals,\n drops,\n finish: last.finish ?? undefined,\n tps: tokensPerSecond(produced, 0, window),\n })\n bucket = []\n }\n\n for (const row of steps) {\n if (row.session_id !== sessionID) {\n close()\n sessionID = row.session_id\n promptIndex = -2\n promptAt = undefined\n previousPromptAt = undefined\n }\n const time = row.created_at ?? row.recorded_at\n const prompts = promptsBySession.get(row.session_id) ?? []\n let index = -1\n for (let candidate = prompts.length - 1; candidate >= 0; candidate -= 1) {\n if ((prompts[candidate] ?? Number.POSITIVE_INFINITY) <= time) {\n index = candidate\n break\n }\n }\n if (index !== promptIndex) {\n close()\n promptIndex = index\n promptAt = index >= 0 ? prompts[index] : undefined\n previousPromptAt = index >= 1 ? prompts[index - 1] : undefined\n }\n bucket.push(row)\n }\n close()\n\n return result.reverse().slice(0, limit)\n}\n\n/**\n * Creation time of the `count`-th newest prompt of a session, or undefined\n * when it has fewer. Used to bound the steps a `turns` read has to load.\n */\nfunction promptFloor(db: Database, sessionID: string, count: number): number | undefined {\n const rows = db\n .query(\"SELECT created_at FROM prompts WHERE session_id = ? AND created_at IS NOT NULL ORDER BY created_at DESC LIMIT ?\")\n .all(sessionID, count) as { created_at: number }[]\n return rows.length === count ? rows[count - 1]!.created_at : undefined\n}\n\n// ---------------------------------------------------------------------------\n// Filter pickers\n// ---------------------------------------------------------------------------\n\nexport type FilterOption = { value: string; steps: number }\n\n/** Distinct models and providers, most used first. */\nexport function filterOptions(db: Database): { models: FilterOption[]; providers: FilterOption[] } {\n const pick = (column: string): FilterOption[] =>\n (\n db\n .query(`SELECT ${column} AS value, COUNT(*) AS steps FROM steps WHERE ${column} IS NOT NULL GROUP BY ${column} ORDER BY steps DESC`)\n .all() as { value: string; steps: number }[]\n ).map((row) => ({ value: row.value, steps: row.steps }))\n return { models: pick(\"model_id\"), providers: pick(\"provider_id\") }\n}\n",
10
+ "import {\n displayTotals,\n formatClock,\n formatCost,\n formatDuration,\n formatLine,\n formatTokens,\n type LineFields,\n type LineLabels,\n type TurnStats,\n type TurnTotalsView,\n} from \"./core\"\nimport type {\n CacheDropRow,\n LatencyReport,\n ModelUsage,\n Overview,\n Range,\n SessionUsage,\n TurnUsage,\n TtlEvidenceRow,\n} from \"./stats/queries\"\nimport type { DbInfo } from \"./stats/maintenance\"\nimport type {\n CacheStats,\n CoverageReport,\n ProviderRow,\n ProviderSummary,\n GapReport,\n LatencyGroupRow,\n PeriodGranularity,\n PeriodRow,\n SwitchRow,\n} from \"./stats/aggregates\"\nimport { modelKey } from \"./stats/queries\"\nimport { sanitizeText } from \"./text\"\n\n/**\n * View models and formatting for the stats panel. Everything here is pure:\n * the panel component feeds query results in and renders the lines out, which\n * keeps the layout testable without a terminal. Lines may carry a `row`, the\n * identity the cursor selects and Enter drills into.\n */\n\nexport const PANEL_NAME = \"usage-insights\"\n\nexport const PANEL_VIEWS = [\"overview\", \"models\", \"providers\", \"cache\", \"periods\", \"latency\", \"sessions\", \"turns\", \"db\"] as const\nexport type PanelView = (typeof PANEL_VIEWS)[number]\n\nexport const RANGE_KEYS = [\"session\", \"today\", \"7d\", \"30d\", \"all\"] as const\nexport type RangeKey = (typeof RANGE_KEYS)[number]\n\nexport const GRANULARITIES = [\"day\", \"week\", \"month\"] as const\nexport type Granularity = (typeof GRANULARITIES)[number]\n\nexport type PanelTone = \"base\" | \"muted\" | \"warning\" | \"accent\"\n\n/** What a selectable line stands for; Enter drills into it. */\nexport type PanelRow = {\n kind: \"model\" | \"session\" | \"period\" | \"provider\" | \"turn\" | \"prune\"\n value: string\n variant?: string | undefined\n}\n\nexport type PanelLine = {\n text: string\n tone?: PanelTone\n row?: PanelRow | undefined\n}\n\n/** The views and ranges, as the command and the panel header understand them. */\nexport function parseArgs(input: string | undefined): { view?: PanelView; range?: RangeKey } {\n const parsed: { view?: PanelView; range?: RangeKey } = {}\n for (const token of (input ?? \"\").split(/\\s+/)) {\n const value = token.trim().toLowerCase()\n if (value === \"\") continue\n if ((PANEL_VIEWS as readonly string[]).includes(value)) parsed.view = value as PanelView\n else if ((RANGE_KEYS as readonly string[]).includes(value)) parsed.range = value as RangeKey\n }\n return parsed\n}\n\nexport function rangeFor(key: RangeKey, now: number, sessionStart?: number | undefined): Range | undefined {\n switch (key) {\n case \"session\":\n return sessionStart === undefined ? undefined : { from: sessionStart }\n case \"today\": {\n const start = new Date(now)\n start.setHours(0, 0, 0, 0)\n return { from: start.getTime() }\n }\n case \"7d\":\n return { from: now - 7 * 24 * 60 * 60 * 1000 }\n case \"30d\":\n return { from: now - 30 * 24 * 60 * 60 * 1000 }\n case \"all\":\n return undefined\n }\n}\n\nexport function rangeLabel(key: RangeKey): string {\n switch (key) {\n case \"session\":\n return \"this session\"\n case \"today\":\n return \"today\"\n case \"7d\":\n return \"7 days\"\n case \"30d\":\n return \"30 days\"\n case \"all\":\n return \"all time\"\n }\n}\n\nexport function nextIn<Value>(values: readonly Value[], current: Value, step: number): Value {\n const index = values.indexOf(current)\n if (index === -1) return values[0]!\n return values[(index + step + values.length) % values.length]!\n}\n\n/** Which turns the `turns` view lists: the open session, or every session. */\nexport type TurnsScope = \"session\" | \"all\"\n\n/**\n * Everything the panel remembers between openings. Stored through the host's\n * durable storage, so the panel reopens where the user left it.\n */\nexport type PanelSettings = {\n view: PanelView\n range: RangeKey\n sort: string\n sortDesc: boolean\n granularity: Granularity\n turnsScope: TurnsScope\n /** Empty string means \"no filter\". */\n modelID: string\n /** The provider the model ran through. */\n providerID: string\n}\n\nexport const DEFAULT_SETTINGS: PanelSettings = {\n view: \"overview\",\n range: \"all\",\n sort: \"\",\n sortDesc: true,\n granularity: \"day\",\n turnsScope: \"session\",\n modelID: \"\",\n providerID: \"\",\n}\n\n/** Merge persisted state with the defaults, dropping anything unrecognized. */\nexport function sanitizeSettings(raw: Partial<PanelSettings> | undefined): PanelSettings {\n const settings = { ...DEFAULT_SETTINGS, ...(raw ?? {}) }\n return {\n view: (PANEL_VIEWS as readonly string[]).includes(settings.view) ? settings.view : DEFAULT_SETTINGS.view,\n range: (RANGE_KEYS as readonly string[]).includes(settings.range) ? settings.range : DEFAULT_SETTINGS.range,\n sort: typeof settings.sort === \"string\" ? settings.sort : DEFAULT_SETTINGS.sort,\n sortDesc: settings.sortDesc === true,\n granularity: (GRANULARITIES as readonly string[]).includes(settings.granularity) ? settings.granularity : DEFAULT_SETTINGS.granularity,\n turnsScope: settings.turnsScope === \"all\" ? \"all\" : \"session\",\n modelID: typeof settings.modelID === \"string\" ? settings.modelID : \"\",\n providerID: typeof settings.providerID === \"string\" ? settings.providerID : \"\",\n }\n}\n\n// ---------------------------------------------------------------------------\n// Primitive formatting\n// ---------------------------------------------------------------------------\n\nexport function formatPercent(value: number | undefined, digits = 1): string {\n return value === undefined ? \"–\" : `${(value * 100).toFixed(digits)}%`\n}\n\nexport function formatCount(value: number): string {\n return Math.round(value).toLocaleString(\"en-US\")\n}\n\nexport function formatMs(value: number | undefined): string {\n if (value === undefined) return \"–\"\n if (value < 1_000) return `${Math.round(value)}ms`\n if (value < 60_000) return `${(value / 1_000).toFixed(1)}s`\n return formatDuration(value)\n}\n\nexport function formatTps(value: number | undefined): string {\n return value === undefined ? \"–\" : `${value.toFixed(1)}/s`\n}\n\n/** A rate that has to fit a sidebar: whole numbers from 10 up. */\nexport function formatTpsCompact(value: number | undefined): string {\n if (value === undefined) return \"–\"\n return value >= 10 ? `${Math.round(value)}/s` : `${value.toFixed(1)}/s`\n}\n\n/** Idle gaps are read at a glance: seconds, minutes, hours, days. */\nexport function formatGap(value: number | undefined): string {\n if (value === undefined) return \"–\"\n if (value < 60_000) return `${Math.round(value / 1_000)}s`\n if (value < 3_600_000) return `${Math.round(value / 60_000)}m`\n if (value < 86_400_000) return `${(value / 3_600_000).toFixed(1)}h`\n return `${(value / 86_400_000).toFixed(1)}d`\n}\n\n/** Cut a value to the width, dropping control characters first. */\nexport function truncate(value: string, width: number): string {\n if (width <= 1) return \"\"\n const clean = sanitizeText(value)\n if (clean.length <= width) return clean\n return `${clean.slice(0, width - 1).trimEnd()}…`\n}\n\n/** Keep both ends of a long id: the family reads first, the `#variant` last. */\nexport function truncateMiddle(value: string, width: number): string {\n if (width <= 1) return \"\"\n const clean = sanitizeText(value)\n if (clean.length <= width) return clean\n const head = Math.ceil((width - 1) / 2)\n const tail = width - 1 - head\n return `${clean.slice(0, head)}…${clean.slice(clean.length - tail)}`\n}\n\n/**\n * Pick the widest column layout that fits the panel. A table that overflows\n * wraps and stops reading as a table, so the columns are chosen, not squeezed.\n */\nexport function tableWidths(width: number, layouts: readonly (readonly number[])[]): number[] {\n for (const layout of layouts) {\n const total = layout.reduce((sum, column) => sum + column, 0) + layout.length - 1\n if (total <= width) return [...layout]\n }\n const last = layouts[layouts.length - 1]\n return last === undefined ? [] : [...last]\n}\n\n/**\n * A model cell keeps the variant visible: two rows of the same family must not\n * read alike after truncation.\n */\nexport function modelCell(modelID: string, variant: string | undefined, width: number): string {\n return truncateMiddle(variant === undefined ? modelID : `${modelID} ${variant}`, width)\n}\n\nexport function padCell(value: string, width: number, align: \"left\" | \"right\" = \"left\"): string {\n if (value.length >= width) return value\n const padding = \" \".repeat(width - value.length)\n return align === \"right\" ? `${padding}${value}` : `${value}${padding}`\n}\n\nexport function formatTableRow(cells: readonly string[], widths: readonly number[], aligns?: readonly (\"left\" | \"right\")[]): string {\n const parts: string[] = []\n for (let index = 0; index < cells.length; index += 1) {\n const width = widths[index] ?? 8\n const align = aligns?.[index] ?? \"left\"\n parts.push(padCell(truncate(cells[index] ?? \"\", width), width, align))\n }\n return parts.join(\" \")\n}\n\nexport function count(n: number, singular: string, plural = `${singular}s`): string {\n return `${formatCount(n)} ${n === 1 ? singular : plural}`\n}\n\n/** Two label/value pairs per row, collapsed to one pair on narrow panels. */\nexport function kvGrid(pairs: readonly (readonly [string, string])[], width: number): PanelLine[] {\n const lines: PanelLine[] = []\n const label = 11\n const column = Math.floor(width / 2)\n for (let index = 0; index < pairs.length; index += 2) {\n const left = pairs[index]!\n const right = pairs[index + 1]\n const leftText = `${padCell(left[0], label)}${left[1]}`\n if (right === undefined) {\n lines.push({ text: leftText, tone: \"base\" })\n continue\n }\n const rightText = `${padCell(right[0], label)}${right[1]}`\n // Both columns have to fit the requested width: the value on the right may\n // be wider than half the panel (the coverage window dates are), and a line\n // that overflows wraps in the terminal instead of ending.\n const gap = Math.max(2, column - leftText.length)\n if (leftText.length + gap + rightText.length <= width) {\n lines.push({ text: `${leftText}${\" \".repeat(gap)}${rightText}`, tone: \"base\" })\n } else {\n lines.push({ text: leftText, tone: \"base\" })\n lines.push({ text: rightText, tone: \"base\" })\n }\n }\n return lines\n}\n\nconst HEADER_TONE: PanelTone = \"accent\"\nconst SPARK = [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\"] as const\n\n/** Fold a series into at most `maxBars` summed buckets, so a sparkline stays a line. */\nexport function reduceSeries(values: readonly number[], maxBars = 48): number[] {\n if (values.length <= maxBars) return [...values]\n const group = Math.ceil(values.length / maxBars)\n const reduced: number[] = []\n for (let index = 0; index < values.length; index += group) {\n reduced.push(values.slice(index, index + group).reduce((sum, value) => sum + value, 0))\n }\n return reduced\n}\n\n/** Text sparkline: trends at a glance without a chart renderable. */\nexport function sparkline(values: readonly number[], maxBars = 48): string {\n if (values.length === 0) return \"\"\n const series = reduceSeries(values, maxBars)\n const max = Math.max(...series)\n if (max <= 0) return SPARK[0]!.repeat(series.length)\n return series.map((value) => SPARK[Math.min(SPARK.length - 1, Math.max(0, Math.round((value / max) * (SPARK.length - 1))))]!).join(\"\")\n}\n\n/**\n * The live line's buckets, so every aggregate the panel shows reconciles with\n * it: `in` is the uncached prompt (fresh input *and* cache writes), `prompt` is\n * what the provider saw, `out` is everything generated (visible *and*\n * reasoning). `write` and `reasoning` are the parts of `in` and `out`.\n */\nexport type UsageBuckets = {\n steps: number\n input: number\n prompt: number\n cached: number\n cacheWrite: number\n output: number\n reasoning: number\n cost: number\n hit: number | undefined\n}\n\nexport function usageBuckets(usage: {\n steps: number\n input: number\n cached: number\n cacheWrite: number\n output: number\n reasoning: number\n cost: number\n}): UsageBuckets {\n const prompt = usage.input + usage.cached + usage.cacheWrite\n return {\n steps: usage.steps,\n input: prompt - usage.cached,\n prompt,\n cached: usage.cached,\n cacheWrite: usage.cacheWrite,\n output: usage.output + usage.reasoning,\n reasoning: usage.reasoning,\n cost: usage.cost,\n hit: prompt > 0 ? usage.cached / prompt : undefined,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Chrome shared by every view\n// ---------------------------------------------------------------------------\n\n/**\n * The view tabs. The active one is bracketed so a single tone still reads\n * clearly; on a narrow panel the names shorten instead of wrapping, and the\n * active view always keeps its full name.\n */\nexport function viewBar(view: PanelView, width = 200): PanelLine {\n // Nine views, nine digits: `0` is free for a tenth.\n const key = (index: number) => String((index + 1) % 10)\n const full = PANEL_VIEWS.map((name, index) => (name === view ? `[${key(index)} ${name}]` : `${key(index)} ${name}`))\n const candidates = [full, PANEL_VIEWS.map((name, index) => (name === view ? `[${key(index)} ${name}]` : `${key(index)} ${name.slice(0, 3)}`))]\n for (const parts of candidates) {\n const text = parts.join(\" \")\n if (text.length <= width) return { text, tone: \"muted\" }\n }\n return { text: `[${PANEL_VIEWS.indexOf(view) + 1} ${view}] · 1-9 or [ ] to switch`, tone: \"muted\" }\n}\n\nexport function keysFor(of: PanelView): string {\n if (of === \"db\") return \"↑↓ pick a retention · enter prune (asks first) · v vacuum · r refresh · 1-9 view · ? help\"\n if (of === \"periods\") return \"↑↓ move · enter drill · b bucket · t range · m/p filter · x clear · 1-9 view · ? help\"\n if (of === \"turns\") return \"↑↓ move · a scope · t range · m/p filter · x clear · 1-9 view · ? help\"\n return \"↑↓ move · enter drill · s sort · t range · m/p filter · x clear · 1-9 view · ? help\"\n}\n\nexport function filterSummary(filters: { modelID?: string | undefined; providerID?: string | undefined }): string | undefined {\n const parts: string[] = []\n if (filters.modelID !== undefined && filters.modelID !== \"\") parts.push(`model=${filters.modelID}`)\n if (filters.providerID !== undefined && filters.providerID !== \"\") parts.push(`provider=${filters.providerID}`)\n return parts.length > 0 ? parts.join(\" · \") : undefined\n}\n\nexport function granularityLabel(granularity: PeriodGranularity): string {\n return granularity === \"day\" ? \"per day\" : granularity === \"week\" ? \"per week\" : \"per month\"\n}\n\n/** Help overlay: the keys and the exact meaning of every number. */\nexport function helpLines(width: number): PanelLine[] {\n const lines: PanelLine[] = [\n { text: \"keys\", tone: HEADER_TONE },\n { text: \" ↑ ↓ j k move the cursor · enter drills in · backspace goes back\" },\n { text: \" 1-9 [ ] choose a view · cycle views (9 is db)\" },\n { text: \" t shift+t previous / next range\" },\n { text: \" s shift+s cycle sort column · reverse\" },\n { text: \" b periods bucket: day → week → month\" },\n { text: \" a turns scope: this session ↔ all sessions\" },\n { text: \" m p filter by model / provider (x clears)\" },\n { text: \" r re-read the database · f full screen · escape close\" },\n { text: \"\" },\n { text: \"numbers · the same buckets as the live line\", tone: HEADER_TONE },\n { text: \" prompt everything the provider saw: cached + fresh + writes\" },\n { text: \" in the uncached part: fresh input + cache writes\" },\n { text: \" cached prompt read from the provider prompt cache\" },\n { text: \" write part of `in`: prompt written into that cache\" },\n { text: \" hit cached / prompt, weighted over the selection\" },\n { text: \" hit p50 median of the per-step hit: spots a split pattern\" },\n { text: \" out everything generated: visible output + reasoning\" },\n { text: \" reasoning part of `out`\" },\n { text: \" cost money, not tokens: a turn sums every step; $0 is left out\" },\n { text: \" wall request → completed step · stream request → last chunk\" },\n { text: \" tps out / streaming seconds, shown as p50\" },\n { text: \" switches how often a step's model or provider differs from the step\" },\n { text: \" before it in the same session (the models view's detail), with\" },\n { text: \" the cache a provider switch cost\" },\n { text: \"\" },\n { text: \"identity\", tone: HEADER_TONE },\n { text: \" provider the provider the model ran through, as OpenCode is configured\" },\n { text: \" (opencode, opencode-go, deepseek, …). A gateway may re-route\" },\n { text: \" a request, but the upstream that ran the inference is not\" },\n { text: \" observable, so the provider is the identity every view shows.\" },\n { text: \"\" },\n { text: \"database\", tone: HEADER_TONE },\n { text: \" the db view shows where the file lives, what it holds and how much\" },\n { text: \" of it is old: ↑↓ pick a retention, enter prunes from its cutoff date,\" },\n { text: \" v gives the space back\" },\n { text: \" local time · SQLite in the OpenCode state directory\" },\n ]\n return lines.map((line) => ({ ...line, text: truncate(line.text, width) }))\n}\n\n// ---------------------------------------------------------------------------\n// Overview\n// ---------------------------------------------------------------------------\n\nfunction usagePairs(usage: Overview | SessionUsage | undefined): (readonly [string, string])[] {\n if (usage === undefined) return []\n const buckets = usageBuckets(usage)\n const pairs: (readonly [string, string])[] = [\n [\"steps\", formatCount(buckets.steps)],\n [\"in\", formatTokens(buckets.input)],\n [\"prompt\", formatTokens(buckets.prompt)],\n [\"cached\", formatTokens(buckets.cached)],\n [\"hit\", formatPercent(buckets.hit)],\n [\"out\", formatTokens(buckets.output)],\n [\"write\", formatTokens(buckets.cacheWrite)],\n ]\n if (buckets.reasoning > 0) pairs.push([\"reasoning\", formatTokens(buckets.reasoning)])\n pairs.push([\"cost\", formatCost(buckets.cost)])\n // The selection's average throughput, the same number the sidebar's session\n // block shows.\n if (\"tps\" in usage && usage.tps !== undefined) pairs.push([\"tps\", formatTps(usage.tps)])\n return pairs\n}\n\nexport function overviewLines(\n data: {\n usage: Overview\n session: Overview | undefined\n /** Child sessions of the open session, when it has any. */\n subagents?: readonly SessionUsage[] | undefined\n range: RangeKey\n activity?: readonly number[] | undefined\n coverage?: CoverageReport | undefined\n },\n width: number,\n): PanelLine[] {\n const lines: PanelLine[] = []\n lines.push({\n text: `${rangeLabel(data.range)} · ${count(data.usage.steps, \"step\")} · ${count(data.usage.sessions, \"session\")} · ${count(data.usage.prompts, \"prompt\")}`,\n tone: \"muted\",\n })\n lines.push(...kvGrid(buildOverviewPairs(data.usage, \"all\"), width))\n if (data.activity !== undefined && data.activity.length > 1) {\n const series = reduceSeries(data.activity)\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: `activity ${sparkline(series)} ${count(series.length, \"bucket\")}`, tone: \"muted\" })\n }\n if (data.session !== undefined) {\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"this session\", tone: HEADER_TONE })\n lines.push(...kvGrid(buildOverviewPairs(data.session, \"session\"), width))\n if (data.subagents !== undefined && data.subagents.length > 0) {\n const steps = data.subagents.reduce((sum, row) => sum + row.steps, 0)\n const cost = data.subagents.reduce((sum, row) => sum + row.cost, 0)\n lines.push({\n text: ` subagents ${count(data.subagents.length, \"session\")} · ${count(steps, \"step\")} · ${formatCost(cost)} (their own sessions)`,\n tone: \"muted\",\n })\n }\n }\n if (data.coverage !== undefined) {\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"data coverage\", tone: HEADER_TONE })\n lines.push(...kvGrid(coveragePairs(data.coverage), width))\n }\n return lines\n}\n\nfunction buildOverviewPairs(usage: Overview, scope: \"all\" | \"session\"): (readonly [string, string])[] {\n const pairs = usagePairs(usage)\n if (scope === \"all\") {\n pairs.push([\"first\", usage.firstAt === undefined ? \"–\" : new Date(usage.firstAt).toLocaleString(\"en-GB\")])\n pairs.push([\"last\", usage.lastAt === undefined ? \"–\" : new Date(usage.lastAt).toLocaleString(\"en-GB\")])\n }\n return pairs\n}\n\nfunction coveragePairs(coverage: CoverageReport): (readonly [string, string])[] {\n const share = (missing: number) => (coverage.steps === 0 ? \"–\" : `${((missing / coverage.steps) * 100).toFixed(1)}%`)\n return [\n [\"models\", `${formatCount(coverage.models)} · unknown ${share(coverage.missingModel)}`],\n [\"providers\", formatCount(coverage.providers)],\n [\"unsettled\", `${formatCount(coverage.unsettled)} · no tps ${share(coverage.missingTps)}`],\n [\n \"window\",\n `${coverage.firstAt === undefined ? \"–\" : new Date(coverage.firstAt).toLocaleString(\"en-GB\")} → ${\n coverage.lastAt === undefined ? \"–\" : new Date(coverage.lastAt).toLocaleString(\"en-GB\")\n }`,\n ],\n ]\n}\n\n// ---------------------------------------------------------------------------\n// Models\n// ---------------------------------------------------------------------------\n\ntype ModelColumns = {\n model: number\n steps: number\n prompt: number\n cached: number\n hit: number\n out: number\n cost: number\n spread: number\n}\n\nfunction modelColumns(width: number): ModelColumns {\n const layouts = [\n { model: 30, steps: 6, prompt: 10, cached: 10, hit: 7, out: 9, cost: 9, spread: 24 },\n { model: 26, steps: 6, prompt: 9, cached: 9, hit: 7, out: 8, cost: 8, spread: 20 },\n { model: 20, steps: 6, prompt: 9, cached: 9, hit: 6, out: 8, cost: 8, spread: 0 },\n { model: 18, steps: 5, prompt: 8, cached: 8, hit: 0, out: 7, cost: 8, spread: 0 },\n { model: 14, steps: 5, prompt: 8, cached: 8, hit: 0, out: 7, cost: 0, spread: 0 },\n ]\n for (const layout of layouts) {\n const total = Object.values(layout).reduce((sum, column) => sum + column, 0) + Object.values(layout).filter((column) => column > 0).length - 1\n if (total <= width) return layout\n }\n return layouts[layouts.length - 1]!\n}\n\nexport type ModelExtras = {\n /** Reuse stats per `modelKey`, computed from the step facts. */\n cache?: Map<string, CacheStats> | undefined\n /** Switches per `modelKey`, computed from the consecutive step pairs. */\n switches?: Map<string, SwitchRow> | undefined\n}\n\n/**\n * Usage per model with the cache it actually got, the providers it ran through\n * and how often its identity changed. One line per model; `providers` keeps the\n * per-provider detail.\n */\nexport function modelsLines(rows: readonly ModelUsage[], width: number, extras?: ModelExtras, limit = 40): PanelLine[] {\n const columns = modelColumns(width)\n const widths: number[] = [columns.model, columns.steps, columns.prompt, columns.cached]\n const aligns: (\"left\" | \"right\")[] = [\"left\", \"right\", \"right\", \"right\"]\n const header: string[] = [\"model\", \"steps\", \"prompt\", \"cached\"]\n if (columns.hit > 0) {\n widths.push(columns.hit)\n aligns.push(\"right\")\n header.push(\"hit\")\n }\n widths.push(columns.out)\n aligns.push(\"right\")\n header.push(\"out\")\n if (columns.cost > 0) {\n widths.push(columns.cost)\n aligns.push(\"right\")\n header.push(\"cost\")\n }\n if (columns.spread > 0) {\n widths.push(columns.spread)\n aligns.push(\"left\")\n header.push(\"providers\")\n }\n const lines: PanelLine[] = [{ text: formatTableRow(header, widths, aligns), tone: HEADER_TONE }]\n\n for (const model of rows.slice(0, limit)) {\n const key = modelKey(model.modelID, model.variant)\n const name = modelCell(model.modelID, model.variant, columns.model)\n const buckets = usageBuckets(model.totals)\n // The same model can come from more than one provider, so the column names\n // them; the detail line below carries the reuse and the change count.\n const providers = unique(model.providers.map((provider) => provider.providerID))\n const cache = extras?.cache?.get(key)\n const switches = extras?.switches?.get(key)\n const cells: string[] = [name, formatCount(buckets.steps), formatTokens(buckets.prompt), formatTokens(buckets.cached)]\n if (columns.hit > 0) cells.push(formatPercent(cache?.hit ?? buckets.hit))\n cells.push(formatTokens(buckets.output))\n if (columns.cost > 0) cells.push(formatCost(buckets.cost))\n if (columns.spread > 0) cells.push(providers.join(\", \"))\n lines.push({\n text: formatTableRow(cells, widths, aligns),\n tone: \"base\",\n row: { kind: \"model\", value: model.modelID, ...(model.variant !== undefined ? { variant: model.variant } : {}) },\n })\n const details: string[] = []\n if (cache !== undefined) details.push(`p50 hit ${formatPercent(cache.hitP50)}`)\n if (switches !== undefined && switches.changes > 0) {\n // The dimensions overlap — a single switch can change both — so the\n // provider count is given as the part of the total that can cost a cache.\n const scope = switches.providerChanges > 0 ? ` (${formatCount(switches.providerChanges)} provider)` : \"\"\n details.push(`${count(switches.changes, \"switch\", \"switches\")}${scope}`)\n if (switches.cacheLost > 0) details.push(`${formatTokens(switches.cacheLost)} lost on a switch`)\n }\n if (details.length > 0) {\n lines.push({ text: truncate(`${\" \".repeat(columns.model + 1)}${details.join(\" · \")}`, width), tone: \"muted\" })\n }\n }\n if (rows.length > limit) lines.push({ text: `… ${count(rows.length - limit, \"more model\")} · narrow the range or filter`, tone: \"muted\" })\n if (rows.length === 0) lines.push({ text: \"no usage recorded for this range\", tone: \"muted\" })\n return lines\n}\n\nfunction unique(values: readonly string[]): string[] {\n return [...new Set(values)]\n}\n\n// ---------------------------------------------------------------------------\n// Providers (model × provider)\n// ---------------------------------------------------------------------------\n\n/**\n * A table column. Header, width, alignment and cell come from the same spec, so\n * dropping an optional column can never shift the others.\n */\ntype ColumnSpec<Row> = {\n header: string\n width: number\n align?: \"left\" | \"right\"\n cell: (row: Row) => string\n /** Columns marked optional are dropped, last one first, until the table fits. */\n optional?: boolean\n}\n\nconst tableWidth = <Row>(columns: readonly ColumnSpec<Row>[]): number => columns.reduce((sum, column) => sum + column.width, 0) + columns.length - 1\n\nfunction fitColumns<Row>(columns: readonly ColumnSpec<Row>[], width: number): ColumnSpec<Row>[] {\n const chosen = [...columns]\n while (chosen.length > 1 && tableWidth(chosen) > width) {\n const optional = [...chosen].reverse().findIndex((column) => column.optional === true)\n if (optional === -1) break\n chosen.splice(chosen.length - 1 - optional, 1)\n }\n return chosen\n}\n\nfunction renderTable<Row>(\n rows: readonly Row[],\n columns: readonly ColumnSpec<Row>[],\n width: number,\n options: { limit: number; empty: string; row?: (row: Row) => PanelRow | undefined; tone?: (row: Row) => PanelTone },\n): PanelLine[] {\n const chosen = fitColumns(columns, width)\n const widths = chosen.map((column) => column.width)\n const aligns = chosen.map((column) => column.align ?? \"left\")\n const alignsTyped = aligns as (\"left\" | \"right\")[]\n const lines: PanelLine[] = [{ text: formatTableRow(chosen.map((column) => column.header), widths, alignsTyped), tone: HEADER_TONE }]\n if (rows.length === 0) lines.push({ text: options.empty, tone: \"muted\" })\n for (const row of rows.slice(0, options.limit)) {\n const identity = options.row?.(row)\n lines.push({\n text: formatTableRow(\n chosen.map((column) => column.cell(row)),\n widths,\n alignsTyped,\n ),\n tone: options.tone?.(row) ?? \"base\",\n ...(identity === undefined ? {} : { row: identity }),\n })\n }\n if (rows.length > options.limit) lines.push({ text: `… ${count(rows.length - options.limit, \"more row\")}`, tone: \"muted\" })\n return lines\n}\n\n/** One row per provider, over every model it served. */\nexport function providerSummaryLines(rows: readonly ProviderSummary[], width: number, limit = 40): PanelLine[] {\n const lines = renderTable(\n rows,\n [\n { header: \"provider\", width: 24, cell: (row) => truncate(row.provider ?? \"– (unknown)\", 24) },\n { header: \"models\", width: 13, align: \"right\", optional: true, cell: (row) => formatCount(row.models.length) },\n { header: \"steps\", width: 6, align: \"right\", cell: (row) => formatCount(row.usage.steps) },\n { header: \"share\", width: 6, align: \"right\", optional: true, cell: (row) => formatPercent(row.share, 0) },\n { header: \"prompt\", width: 10, align: \"right\", cell: (row) => formatTokens(row.cache.prompt) },\n { header: \"cached\", width: 10, align: \"right\", cell: (row) => formatTokens(row.cache.cached) },\n { header: \"hit\", width: 7, align: \"right\", optional: true, cell: (row) => formatPercent(row.cache.hit) },\n { header: \"tps p50\", width: 8, align: \"right\", optional: true, cell: (row) => formatTps(row.tps) },\n { header: \"wall\", width: 8, align: \"right\", optional: true, cell: (row) => formatMs(row.wall) },\n { header: \"cost\", width: 9, align: \"right\", optional: true, cell: (row) => formatCost(row.usage.cost) },\n ],\n width,\n {\n limit,\n empty: \"no usage recorded for this range\",\n // A step recorded without a provider is not a row to drill into.\n row: (row) => (row.provider === undefined ? undefined : { kind: \"provider\", value: row.provider }),\n tone: (row) => (row.provider === undefined ? \"muted\" : \"base\"),\n },\n )\n return lines\n}\n\n/** One row per model × provider: the providers each model ran through. */\nexport function providerLines(rows: readonly ProviderRow[], width: number, limit = 40): PanelLine[] {\n return renderTable(\n rows,\n [\n { header: \"model\", width: 26, cell: (row) => modelCell(row.modelID, row.variant, 26) },\n { header: \"provider\", width: 22, cell: (row) => truncate(row.provider ?? \"– (unknown)\", 22) },\n { header: \"steps\", width: 6, align: \"right\", cell: (row) => formatCount(row.usage.steps) },\n { header: \"share\", width: 6, align: \"right\", optional: true, cell: (row) => formatPercent(row.share, 0) },\n { header: \"prompt\", width: 10, align: \"right\", cell: (row) => formatTokens(row.cache.prompt) },\n { header: \"cached\", width: 10, align: \"right\", cell: (row) => formatTokens(row.cache.cached) },\n { header: \"hit\", width: 7, align: \"right\", optional: true, cell: (row) => formatPercent(row.cache.hit) },\n { header: \"tps p50\", width: 8, align: \"right\", optional: true, cell: (row) => formatTps(row.tps) },\n { header: \"wall\", width: 8, align: \"right\", optional: true, cell: (row) => formatMs(row.wall) },\n { header: \"cost\", width: 9, align: \"right\", optional: true, cell: (row) => formatCost(row.usage.cost) },\n ],\n width,\n {\n limit,\n empty: \"no usage recorded for this range\",\n row: (row) => ({ kind: \"model\", value: row.modelID, ...(row.variant !== undefined ? { variant: row.variant } : {}) }),\n tone: (row) => (row.provider === undefined ? \"muted\" : \"base\"),\n },\n )\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\n/** One model's reuse row, shared by the models and cache views. */\nexport type CacheModelRow = {\n usage: { modelID: string; variant: string | undefined }\n cache: CacheStats\n}\n\nexport function cacheLines(\n data: {\n drops: readonly CacheDropRow[]\n evidence: readonly TtlEvidenceRow[]\n gap?: GapReport | undefined\n models?: readonly CacheModelRow[] | undefined\n },\n width: number,\n): PanelLine[] {\n const lines: PanelLine[] = []\n const total = data.drops.reduce((sum, drop) => sum + drop.tokens, 0)\n lines.push({ text: `reuse losses ${count(data.drops.length, \"event\")} · ${formatTokens(total)} tokens`, tone: total > 0 ? \"warning\" : \"base\" })\n for (const reason of [\"expired\", \"compaction\", \"unknown\"] as const) {\n const group = data.drops.filter((drop) => drop.reason === reason)\n if (group.length === 0) continue\n const tokens = group.reduce((sum, drop) => sum + drop.tokens, 0)\n lines.push({ text: ` ${padCell(reason, 11)}${formatCount(group.length)} · ${formatTokens(tokens)}`, tone: reason === \"expired\" ? \"warning\" : \"muted\" })\n }\n if (data.drops.length === 0) lines.push({ text: \" none: prompt cache held through every gap\", tone: \"muted\" })\n\n if (data.models !== undefined && data.models.length > 0) {\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"reuse by model\", tone: HEADER_TONE })\n const widths = width >= 100 ? [30, 7, 11, 10, 8, 8, 10] : [18, 6, 9, 9, 7, 7, 9]\n lines.push({ text: formatTableRow([\"model\", \"steps\", \"prompt\", \"cached\", \"write\", \"hit\", \"hit p50\"], widths, [\"left\", \"right\", \"right\", \"right\", \"right\", \"right\", \"right\"]), tone: \"muted\" })\n for (const row of data.models.slice(0, 10)) {\n const name = modelCell(row.usage.modelID, row.usage.variant, widths[0]!)\n lines.push({\n text: formatTableRow(\n [name, formatCount(row.cache.steps), formatTokens(row.cache.prompt), formatTokens(row.cache.cached), formatTokens(row.cache.cacheWrite), formatPercent(row.cache.hit), formatPercent(row.cache.hitP50)],\n widths,\n [\"left\", \"right\", \"right\", \"right\", \"right\", \"right\", \"right\"],\n ),\n tone: \"base\",\n row: { kind: \"model\", value: row.usage.modelID, ...(row.usage.variant !== undefined ? { variant: row.usage.variant } : {}) },\n })\n }\n }\n\n if (data.gap !== undefined && data.gap.samples > 0) {\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"cache survival against idle time\", tone: HEADER_TONE })\n const widths = [9, 8, 9, 9, 10]\n lines.push({ text: formatTableRow([\"gap\", \"pairs\", \"kept\", \"drops\", \"lost\"], widths, [\"left\", \"right\", \"right\", \"right\", \"right\"]), tone: \"muted\" })\n for (const bucket of data.gap.buckets) {\n if (bucket.samples === 0) continue\n lines.push({\n text: formatTableRow(\n [bucket.label, formatCount(bucket.samples), formatPercent(bucket.retainedRatio, 0), formatCount(bucket.drops), formatTokens(bucket.lostTokens)],\n widths,\n [\"left\", \"right\", \"right\", \"right\", \"right\"],\n ),\n tone: bucket.drops > 0 ? \"warning\" : \"base\",\n })\n }\n if (data.gap.providers.length > 1) {\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"reuse kept, by provider\", tone: HEADER_TONE })\n const providerWidths = [26, 8, 9, 9]\n lines.push({ text: formatTableRow([\"provider\", \"pairs\", \"kept\", \"lost\"], providerWidths, [\"left\", \"right\", \"right\", \"right\"]), tone: \"muted\" })\n for (const row of data.gap.providers.slice(0, 8)) {\n lines.push({\n text: formatTableRow([row.provider, formatCount(row.samples), formatPercent(row.retainedRatio, 0), formatTokens(row.lostTokens)], providerWidths, [\"left\", \"right\", \"right\", \"right\"]),\n tone: \"base\",\n })\n }\n }\n }\n\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"observed cache TTL bounds\", tone: HEADER_TONE })\n if (data.evidence.length === 0) {\n lines.push({ text: \" no comparable step pairs yet\", tone: \"muted\" })\n } else {\n const widths = [Math.max(18, Math.floor(width * 0.4)), 14, 12, 9]\n lines.push({ text: formatTableRow([\"model\", \"hits up to\", \"drops from\", \"samples\"], widths, [\"left\", \"right\", \"right\", \"right\"]), tone: \"muted\" })\n for (const row of data.evidence.slice(0, 10)) {\n lines.push({\n text: formatTableRow(\n [`${row.providerID}/${row.modelID}`, formatGap(row.maxHitGapMs), formatGap(row.minDropGapMs), formatCount(row.samples)],\n widths,\n [\"left\", \"right\", \"right\", \"right\"],\n ),\n tone: \"base\",\n })\n }\n }\n\n if (data.drops.length > 0) {\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"recent losses\", tone: HEADER_TONE })\n for (const drop of [...data.drops].sort((a, b) => b.at - a.at).slice(0, 8)) {\n const when = new Date(drop.at).toLocaleTimeString(\"en-GB\")\n lines.push({\n text: formatTableRow(\n [when, `${drop.providerID ?? \"\"}/${drop.modelID ?? \"\"}`, `−${formatTokens(drop.tokens)}`, `${formatGap(drop.gapMs)} idle · ${drop.reason}`],\n [10, Math.max(20, Math.floor(width * 0.42)), 10, 24],\n [\"left\", \"left\", \"right\", \"left\"],\n ),\n tone: drop.reason === \"expired\" ? \"warning\" : \"base\",\n })\n }\n }\n return lines\n}\n\n// ---------------------------------------------------------------------------\n// Periods\n// ---------------------------------------------------------------------------\n\nexport function periodLines(rows: readonly PeriodRow[], width: number, granularity: PeriodGranularity, limit = 40): PanelLine[] {\n const lines: PanelLine[] = []\n const totals = rows.reduce(\n (sum, row) => {\n sum.steps += row.usage.steps\n sum.sessions += row.sessions\n sum.input += row.usage.input\n sum.cached += row.usage.cached\n sum.cacheWrite += row.usage.cacheWrite\n sum.output += row.usage.output\n sum.reasoning += row.usage.reasoning\n sum.cost += row.usage.cost\n return sum\n },\n { steps: 0, sessions: 0, input: 0, cached: 0, cacheWrite: 0, output: 0, reasoning: 0, cost: 0 },\n )\n const overall = usageBuckets(totals)\n lines.push({\n text: `${granularityLabel(granularity)} · ${count(rows.length, \"period\")} · ${count(totals.steps, \"step\")} · hit ${formatPercent(overall.hit)}`,\n tone: \"muted\",\n })\n if (rows.length > 1) {\n lines.push({ text: `steps ${sparkline(rows.map((row) => row.usage.steps))}`, tone: \"muted\" })\n lines.push({ text: `cached ${sparkline(rows.map((row) => row.usage.cached))}`, tone: \"muted\" })\n lines.push({ text: `cost ${sparkline(rows.map((row) => Math.round(row.usage.cost * 10_000)))}`, tone: \"muted\" })\n }\n const withSessions = tableWidths(width, [[12, 7, 7, 11, 10, 7, 9, 9]]).length === 8\n const widths = withSessions ? [12, 7, 7, 11, 10, 7, 9, 9] : [12, 7, 11, 10, 7, 9, 9]\n const aligns: (\"left\" | \"right\")[] = widths.map((_, index) => (index === 0 ? \"left\" : \"right\"))\n const header = withSessions ? [\"period\", \"steps\", \"sess\", \"prompt\", \"cached\", \"hit\", \"out\", \"cost\"] : [\"period\", \"steps\", \"prompt\", \"cached\", \"hit\", \"out\", \"cost\"]\n lines.push({ text: formatTableRow(header, widths, aligns), tone: HEADER_TONE })\n\n const newest = [...rows].sort((left, right) => right.at - left.at).slice(0, limit)\n for (const row of newest) {\n const buckets = usageBuckets(row.usage)\n const cells = [\n row.label,\n formatCount(row.steps),\n ...(withSessions ? [formatCount(row.sessions)] : []),\n formatTokens(buckets.prompt),\n formatTokens(buckets.cached),\n formatPercent(buckets.hit, 0),\n formatTokens(buckets.output),\n formatCost(buckets.cost),\n ]\n lines.push({ text: formatTableRow(cells, widths, aligns), tone: \"base\", row: { kind: \"period\", value: row.key } })\n }\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({\n text: formatTableRow(\n [\n \"total\",\n formatCount(totals.steps),\n ...(withSessions ? [formatCount(totals.sessions)] : []),\n formatTokens(overall.prompt),\n formatTokens(overall.cached),\n formatPercent(overall.hit, 0),\n formatTokens(overall.output),\n formatCost(overall.cost),\n ],\n widths,\n aligns,\n ),\n tone: HEADER_TONE,\n })\n if (rows.length === 0) lines.push({ text: \"no usage recorded for this range\", tone: \"muted\" })\n return lines\n}\n\n// ---------------------------------------------------------------------------\n// Latency\n// ---------------------------------------------------------------------------\n\nexport function latencyLines(report: LatencyReport, width: number, groups?: readonly LatencyGroupRow[] | undefined, limit = 12): PanelLine[] {\n const lines: PanelLine[] = []\n lines.push({\n text: formatTableRow(\n [\"wall\", \"p50\", formatMs(report.wall.p50), \"p90\", formatMs(report.wall.p90), \"p99\", formatMs(report.wall.p99), `n ${formatCount(report.wall.count)}`],\n [10, 4, 9, 4, 9, 4, 9, 12],\n ),\n tone: \"base\",\n })\n lines.push({\n text: formatTableRow([\"stream\", \"p50\", formatMs(report.stream.p50), \"p90\", formatMs(report.stream.p90), \"p99\", formatMs(report.stream.p99)], [10, 4, 9, 4, 9, 4, 9]),\n tone: \"base\",\n })\n lines.push({\n text: formatTableRow([\"throughput\", \"p50\", formatTps(report.tps.p50), \"p90\", formatTps(report.tps.p90), \"p99\", formatTps(report.tps.p99)], [10, 4, 9, 4, 9, 4, 9]),\n tone: \"base\",\n })\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({\n text: truncate(\"wall = request to completed · stream = request to last chunk · throughput = output+reasoning tokens/s\", width),\n tone: \"muted\",\n })\n if (report.wall.count === 0) lines.push({ text: \"no completed steps for this range\", tone: \"muted\" })\n\n if (groups !== undefined && groups.length > 0) {\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"by group\", tone: HEADER_TONE })\n const widths = width >= 110 ? [24, 6, 9, 9, 9, 9, 9, 9] : [16, 5, 8, 8, 8, 8, 8, 8]\n const aligns: (\"left\" | \"right\")[] = [\"left\", \"right\", \"right\", \"right\", \"right\", \"right\", \"right\", \"right\"]\n lines.push({ text: formatTableRow([\"group\", \"n\", \"wall p50\", \"wall p90\", \"stream\", \"tps p50\", \"tps p90\", \"cost\"], widths, aligns), tone: \"muted\" })\n for (const row of groups.slice(0, limit)) {\n const thin = row.count < 5\n lines.push({\n text: formatTableRow(\n [row.label, formatCount(row.count), formatMs(row.wall.p50), formatMs(row.wall.p90), formatMs(row.stream.p50), formatTps(row.tps.p50), formatTps(row.tps.p90), formatCost(row.usage.cost)],\n widths,\n aligns,\n ),\n tone: thin ? \"muted\" : \"base\",\n row: { kind: \"model\", value: row.key },\n })\n }\n lines.push({ text: \"n < 5 is dimmed: too few samples to compare\", tone: \"muted\" })\n }\n return lines\n}\n\n// ---------------------------------------------------------------------------\n// Sessions\n// ---------------------------------------------------------------------------\n\nexport function sessionsLines(\n rows: readonly SessionUsage[],\n width: number,\n options?: { limit?: number | undefined; rollup?: { subagentSteps: number; subagentCost: number } | undefined },\n): PanelLine[] {\n const limit = options?.limit ?? 20\n const widths = [Math.max(18, Math.floor(width * 0.36)), 7, 11, 10, 7, 9, 9]\n const aligns: (\"left\" | \"right\")[] = [\"left\", \"right\", \"right\", \"right\", \"right\", \"right\", \"right\"]\n const lines: PanelLine[] = [\n { text: formatTableRow([\"title\", \"steps\", \"prompt\", \"cached\", \"hit\", \"out\", \"cost\"], widths, aligns), tone: HEADER_TONE },\n ]\n const byID = new Map(rows.map((row) => [row.sessionID, row]))\n const ordered = rows.filter((row) => row.parentID === undefined || !byID.has(row.parentID))\n const children = rows.filter((row) => row.parentID !== undefined && byID.has(row.parentID))\n const orderedAll = [...ordered, ...children]\n\n for (const row of orderedAll.slice(0, limit)) {\n const child = row.parentID !== undefined && byID.has(row.parentID)\n const title = truncate(row.title ?? row.sessionID, Math.max(8, widths[0]! - (child ? 2 : 0)))\n const buckets = usageBuckets(row)\n lines.push({\n text: formatTableRow(\n [\n `${child ? \"↳ \" : \"\"}${title}`,\n formatCount(buckets.steps),\n formatTokens(buckets.prompt),\n formatTokens(buckets.cached),\n formatPercent(buckets.hit, 0),\n formatTokens(buckets.output),\n formatCost(buckets.cost),\n ],\n widths,\n aligns,\n ),\n tone: child ? \"muted\" : \"base\",\n row: { kind: \"session\", value: row.sessionID },\n })\n const details = [row.directory ?? \"\", row.models.length > 1 ? count(row.models.length, \"model\") : \"\"].filter((part) => part !== \"\").join(\" · \")\n lines.push({ text: truncate(`${\" \".repeat(child ? 2 : 0)} ${details}`, width), tone: \"muted\" })\n }\n if (options?.rollup !== undefined && options.rollup.subagentSteps > 0) {\n const share = rows.reduce((sum, row) => sum + row.steps, 0)\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({\n text: `subagents ${formatCount(options.rollup.subagentSteps)} steps (${formatPercent(share > 0 ? options.rollup.subagentSteps / share : undefined)}) · ${formatCost(\n options.rollup.subagentCost,\n )}`,\n tone: \"muted\",\n })\n }\n if (rows.length > limit) lines.push({ text: `… ${count(rows.length - limit, \"more session\")} · narrow the range`, tone: \"muted\" })\n if (rows.length === 0) lines.push({ text: \"no sessions recorded for this range\", tone: \"muted\" })\n return lines\n}\n\n// ---------------------------------------------------------------------------\n// Database\n// ---------------------------------------------------------------------------\n\n/** The database view: where it lives, what it holds, and the two cleanups. */\nexport function dbLines(data: { info: DbInfo; busy?: boolean | undefined; now: number }, width: number): PanelLine[] {\n const lines: PanelLine[] = []\n const size = (bytes: number) => `${(bytes / 1_048_576).toFixed(1)} MB`\n lines.push({ text: truncate(data.info.path, width), tone: \"base\" })\n lines.push({\n text: `${size(data.info.bytes)}${data.info.walBytes === undefined ? \"\" : ` + ${size(data.info.walBytes)} wal`} · schema v${data.info.schemaVersion}`,\n tone: \"muted\",\n })\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"what it holds\", tone: HEADER_TONE })\n for (const table of data.info.tables) {\n lines.push({ text: ` ${padCell(table.name, 12)}${formatCount(table.rows)} rows`, tone: table.rows === 0 ? \"muted\" : \"base\" })\n }\n lines.push({\n text: ` window ${data.info.firstAt === undefined ? \"–\" : new Date(data.info.firstAt).toLocaleDateString(\"en-GB\")} → ${\n data.info.lastAt === undefined ? \"–\" : new Date(data.info.lastAt).toLocaleDateString(\"en-GB\")\n }`,\n tone: \"muted\",\n })\n\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"clean up\", tone: HEADER_TONE })\n // The retention rows are the only selectable lines here, so the hint says how\n // to pick one and what Enter does: the row is a cutoff, not a count.\n lines.push({ text: truncate(\" ↑↓ pick a retention · enter prunes the steps older than it (asks first) · v gives the space back\", width), tone: \"muted\" })\n const widths = [10, 13, 10]\n lines.push({ text: formatTableRow([\"keep\", \"prunes\", \"older than\"], widths, [\"left\", \"right\", \"left\"]), tone: \"muted\" })\n for (const option of data.info.removable) {\n const before = new Date(data.now - option.days * 24 * 60 * 60 * 1000).toLocaleDateString(\"en-GB\")\n lines.push({\n text: formatTableRow([`${option.days} days`, `${formatCount(option.steps)} steps`, before], widths, [\"left\", \"right\", \"left\"]),\n tone: option.steps === 0 ? \"muted\" : \"base\",\n row: { kind: \"prune\", value: String(option.days) },\n })\n }\n if (data.busy === true) {\n lines.push({ text: \"\", tone: \"muted\" })\n lines.push({ text: \"the recorder is writing: try the vacuum again in a moment\", tone: \"warning\" })\n }\n return lines\n}\n\n// ---------------------------------------------------------------------------\n// Turns\n// ---------------------------------------------------------------------------\n\nexport type LineFormatOptions = {\n labels?: Partial<LineLabels> | undefined\n fields?: Partial<LineFields> | undefined\n turnTotals?: TurnTotalsView | undefined\n}\n\n/** The line's own view of a recorded turn, so both render identically. */\nexport function turnStats(turn: TurnUsage): TurnStats {\n return {\n endAt: turn.endAt,\n startAt: turn.promptAt ?? turn.endAt,\n promptAt: turn.promptAt,\n promptGapMs: turn.promptGapMs,\n providerID: turn.providerID,\n modelID: turn.modelID,\n variant: turn.variant,\n providerChanged: turn.providerChanged,\n modelChanged: turn.modelChanged,\n totals: turn.totals,\n latest: turn.latest,\n session: undefined,\n drops: turn.drops.map((drop) => ({ messageID: drop.messageID, tokens: drop.tokens, prevCached: 0, gapMs: drop.gapMs })),\n cacheDropTokens: turn.drops.reduce((sum, drop) => sum + drop.tokens, 0),\n cacheExpired: turn.drops.length > 0,\n steps: turn.steps,\n }\n}\n\n/**\n * Per-prompt history: one line per finished turn, rendered by the same\n * formatter as the live line (without the session segment, which would repeat\n * the same totals on every row).\n */\nexport function turnsLines(rows: readonly TurnUsage[], width: number, options?: LineFormatOptions & { sessionTitles?: Map<string, string> | undefined }): PanelLine[] {\n const lines: PanelLine[] = []\n const warning = rows.filter((turn) => turn.drops.length > 0).length\n lines.push({\n text: `${count(rows.length, \"turn\")} · newest first${warning > 0 ? ` · ${count(warning, \"turn\")} with a cache loss` : \"\"}`,\n tone: \"muted\",\n })\n const showSession = options?.sessionTitles !== undefined\n for (const turn of rows) {\n const line = formatLine(turnStats(turn), {\n ...options,\n fields: { ...options?.fields, session: false },\n })\n const title = showSession ? `${truncate(options!.sessionTitles!.get(turn.sessionID) ?? turn.sessionID, 18)} ` : \"\"\n lines.push({\n text: truncate(`${title}${line}`, width),\n tone: turn.drops.length > 0 || turn.providerChanged ? \"warning\" : \"base\",\n row: { kind: \"turn\", value: turn.sessionID },\n })\n }\n if (rows.length === 0) lines.push({ text: \"no finished turns for this range\", tone: \"muted\" })\n return lines\n}\n\n/**\n * The accumulated usage of one session, as compact lines for the sidebar. The\n * buckets are the live line's, so the sidebar, the line and the panel agree.\n */\nexport function sessionSummaryLines(usage: Overview, budget = 34): PanelLine[] {\n const buckets = usageBuckets(usage)\n const steps = formatCount(buckets.steps)\n const input = formatTokens(buckets.input)\n const cached = formatTokens(buckets.cached)\n const out = formatTokens(buckets.output)\n const hit = formatPercent(buckets.hit)\n const tps = formatTpsCompact(usage.tps)\n const cost = formatCost(buckets.cost)\n // The same order as a turn's three lines: how much was sent (`in`, `cached`),\n // how much came back (`out`), then `hit`, the rate and the cost last.\n const first = [\n ` ${steps} steps · in ${input}`,\n ` ${steps} steps`,\n ].find((text) => text.length <= budget)\n const second = [\n ` cached ${cached} · out ${out}`,\n ` cached ${cached}`,\n ].find((text) => text.length <= budget)\n const last = [\n ` hit ${hit} · ${tps} · ${cost}`,\n ` hit ${hit} · ${cost}`,\n ` ${cost}`,\n ].find((text) => text.length <= budget)\n return [\n { text: truncate(\"session\", budget), tone: HEADER_TONE },\n { text: truncate(first ?? ` ${steps} steps`, budget), tone: \"muted\" },\n { text: truncate(second ?? ` cached ${cached}`, budget), tone: \"base\" },\n { text: truncate(last ?? ` ${cost}`, budget), tone: \"base\" },\n { text: \"\", tone: \"muted\" },\n ]\n}\n\n/**\n * The sidebar history: a compact block per turn — when and through which\n * provider, then the prompt volumes, then the cache hit, the rate and the cost.\n *\n * The model gets a line of its own at both ends of the block and on every turn\n * that changed it. The ends matter because the reader cannot tell whether a\n * block is the whole history or a window of a long session: the newest turn\n * names the model in use now, the oldest names the one the window started with,\n * and everything between them is read against the nearest line below it.\n */\nexport function sidebarLines(rows: readonly TurnUsage[], options?: LineFormatOptions & { budget?: number | undefined }): PanelLine[] {\n const budget = options?.budget ?? 34\n const labels = { input: options?.labels?.input ?? \"in\", output: options?.labels?.output ?? \"out\" }\n const lines: PanelLine[] = []\n for (const [index, turn] of rows.entries()) {\n const clock = turn.endAt !== undefined ? formatClock(turn.endAt).slice(0, 5) : \"--:--\"\n const provider = turn.providerID ?? \"unknown\"\n const display = displayTotals(turn.latest)\n const input = formatTokens(display.input)\n const prompt = formatTokens(display.prompt)\n const output = formatTokens(display.output)\n const cost = formatCost(turn.totals.cost)\n // The cache hit of the turn, on the same step the numbers come from. One\n // decimal, with a trailing `.0` dropped: the sidebar has no room to spare.\n const hit = formatPercent(display.prompt > 0 ? display.cached / display.prompt : undefined).replace(/\\.0%$/, \"%\")\n const tps = turn.tps === undefined ? undefined : formatTpsCompact(turn.tps)\n const suffix = turn.drops.length > 0 ? \" · ⚠\" : \"\"\n // Three compact lines: when and through what, the prompt volumes, then what\n // the turn cost — in tokens per second and in money — and how much of the\n // prompt came from cache.\n const volumes = [\n ` ${labels.input} ${input} (${prompt}) · ${labels.output} ${output}`,\n ` ${labels.input} ${input} · ${labels.output} ${output}`,\n ` ${labels.input} ${input}`,\n ]\n const facts = [\n ` hit ${hit}${tps === undefined ? \"\" : ` · ${tps}`} · ${cost}${suffix}`,\n ` hit ${hit}${tps === undefined ? \"\" : ` · ${tps}`}${suffix}`,\n ` ${tps === undefined ? \"\" : `${tps} · `}${cost}${suffix}`,\n ` hit ${hit}${suffix}`,\n ]\n lines.push({ text: truncate(`${clock} ${provider}`, budget), tone: turn.providerChanged ? \"warning\" : \"base\" })\n // The model appears at both ends of the block (the newest turn is the state\n // now, the oldest anchors the window) and on every turn that changed it: the\n // provider alone cannot say which model actually ran.\n const edge = index === 0 || index === rows.length - 1\n if (edge || turn.modelChanged || turn.providerChanged) {\n const model = turn.modelID === undefined ? \"unknown\" : `${turn.modelID}${turn.variant === undefined ? \"\" : ` ${turn.variant}`}`\n lines.push({ text: truncate(` ${model}`, budget), tone: \"muted\" })\n }\n lines.push({\n text: truncate(volumes.find((text) => text.length <= budget) ?? volumes[volumes.length - 1]!, budget),\n tone: \"muted\",\n })\n lines.push({\n text: truncate(facts.find((text) => text.length <= budget) ?? facts[facts.length - 1]!, budget),\n tone: turn.drops.length > 0 ? \"warning\" : \"muted\",\n })\n }\n if (rows.length === 0) lines.push({ text: \"no finished turns yet\", tone: \"muted\" })\n return lines\n}\n",
11
+ "import { effect as _$effect } from \"@opentui/solid\";\nimport { insertNode as _$insertNode } from \"@opentui/solid\";\nimport { insert as _$insert } from \"@opentui/solid\";\nimport { createComponent as _$createComponent } from \"@opentui/solid\";\nimport { use as _$use } from \"@opentui/solid\";\nimport { setProp as _$setProp } from \"@opentui/solid\";\nimport { createElement as _$createElement } from \"@opentui/solid\";\n/** @jsxImportSource @opentui/solid */\n\nimport { TextAttributes } from \"@opentui/core\";\nimport { For, createMemo, createRenderEffect, createSignal } from \"solid-js\";\nimport { buildPanel, defaultSort, nextSort } from \"./panel-build\";\nimport { GRANULARITIES, PANEL_VIEWS, RANGE_KEYS, filterSummary, formatCount, helpLines, keysFor, nextIn, rangeLabel, sanitizeSettings, viewBar } from \"./panel-data\";\nimport { filterOptions } from \"./stats/queries\";\nimport { dbInfo, pruneOlderThan, vacuum } from \"./stats/maintenance\";\n\n/**\n * The `/usage` panel. One read-only view over the stats database at a time,\n * driven entirely from the keyboard: a cursor over the selectable lines,\n * Enter drills into the row under it, and the views, range, sort, bucket and\n * filters are part of the state the host persists between openings.\n *\n * The panel owns input while it is open, so every key lives in the layer this\n * component registers — the prompt is not involved.\n */\n\nexport function Panel(props) {\n const [anchor, setAnchor] = createSignal(undefined);\n const [detail, setDetail] = createSignal(undefined);\n const [help, setHelp] = createSignal(false);\n const [tick, setTick] = createSignal(0);\n // Set when a vacuum could not take the write lock: the recorder is mid-write.\n const [busy, setBusy] = createSignal(false);\n let scroller;\n const width = () => Math.max(60, props.input.width - 4);\n const view = () => sanitizeSettings(props.settings).view;\n const settings = () => sanitizeSettings(props.settings);\n const filters = () => {\n const current = settings();\n return {\n ...(current.modelID !== \"\" ? {\n modelID: current.modelID\n } : {}),\n ...(current.providerID !== \"\" ? {\n provider: current.providerID\n } : {})\n };\n };\n const rowKey = row => `${row.kind}:${row.value}${row.variant !== undefined ? `#${row.variant}` : \"\"}`;\n\n /**\n * What the file holds. Reading it stats the file and counts rows in every\n * table, so only the `db` view (and the prune confirmation) asks for it: the\n * other views must not pay for it on every rebuild.\n */\n const database = createMemo(() => {\n tick();\n const handle = props.deps.db();\n if (handle === undefined) return undefined;\n try {\n return dbInfo(handle, props.deps.dbPath(), props.deps.now());\n } catch {\n return undefined;\n }\n });\n const content = createMemo(() => {\n tick();\n props.deps.refresh?.();\n const current = settings();\n if (help()) return helpLines(width());\n const db = props.deps.db();\n if (db === undefined) {\n return [{\n text: `stats database is not readable at ${props.deps.dbPath()}; enable the usage-insights server plugin in opencode.json`,\n tone: \"warning\"\n }];\n }\n const info = current.view === \"db\" ? database() : undefined;\n const request = {\n db,\n view: current.view,\n range: current.range,\n filters: filters(),\n granularity: current.granularity,\n turnsScope: current.turnsScope,\n sort: current.sort,\n sortDesc: current.sortDesc,\n sessionID: props.input.sessionID,\n sessionStart: props.deps.sessionStart(props.input.sessionID),\n now: props.deps.now(),\n format: props.format,\n ...(detail() !== undefined ? {\n detail: detail()\n } : {}),\n ...(info !== undefined ? {\n database: info\n } : {}),\n busy: busy(),\n width: width()\n };\n return buildPanel(request);\n });\n const header = createMemo(() => {\n const current = settings();\n const filters = filterSummary(current);\n const where = detail() !== undefined ? \" · backspace returns\" : filters === undefined ? \"\" : ` · ${filters}`;\n return [{\n text: `usage insights · ${rangeLabel(current.range)}${where}`,\n tone: \"accent\"\n }, viewBar(current.view, width()), {\n text: \"\",\n tone: \"muted\"\n }];\n });\n const selectable = createMemo(() => {\n const list = [];\n const lines = content();\n for (let index = 0; index < lines.length; index += 1) {\n const row = lines[index].row;\n if (row !== undefined) list.push({\n key: rowKey(row),\n index,\n row\n });\n }\n return list;\n });\n const cursor = createMemo(() => {\n const list = selectable();\n if (list.length === 0) return -1;\n const key = anchor();\n const found = key === undefined ? -1 : list.findIndex(entry => entry.key === key);\n return found === -1 ? 0 : found;\n });\n createRenderEffect(() => {\n const list = selectable();\n const index = cursor();\n if (index < 0) return;\n const entry = list[index];\n if (entry === undefined) return;\n scroller?.scrollChildIntoView?.(`line-${entry.index}`);\n });\n function move(delta) {\n const list = selectable();\n if (list.length === 0) return;\n const next = Math.min(list.length - 1, Math.max(0, cursor() + delta));\n setAnchor(list[next].key);\n }\n function moveEdge(toEnd) {\n const list = selectable();\n if (list.length === 0) return;\n setAnchor(list[toEnd ? list.length - 1 : 0].key);\n }\n function drill() {\n const entry = selectable()[cursor()];\n if (entry === undefined) return;\n if (entry.row.kind === \"prune\") {\n void prune(Number.parseInt(entry.row.value, 10));\n return;\n }\n setDetail(entry.row);\n setAnchor(undefined);\n }\n\n /**\n * True while the session is running. The recorder writes on the agent's own\n * event path, so a maintenance action that holds the write lock for a while\n * would make the agent wait: it is refused instead, before it is even offered.\n */\n function sessionRunning() {\n try {\n return props.ctx.data.session.status(props.input.sessionID) === \"running\";\n } catch {\n return false;\n }\n }\n\n /** Delete the recorded usage older than `days`, after asking. */\n async function prune(days) {\n if (!Number.isFinite(days)) return;\n if (sessionRunning()) {\n props.ctx.ui.toast.show({\n message: \"the session is running: prune again when it is idle\",\n variant: \"warning\"\n });\n return;\n }\n const info = database();\n const steps = info?.removable.find(option => option.days === days)?.steps;\n const confirmed = await props.ctx.ui.dialog.confirm({\n title: `Prune older than ${days} days`,\n message: `${steps === undefined ? \"The recorded usage\" : `${steps} of ${formatCount(info?.tables.find(table => table.name === \"steps\")?.rows ?? 0)} steps`} would be deleted. This cannot be undone.`,\n label: {\n confirm: \"Prune\",\n cancel: \"Keep\"\n }\n });\n if (confirmed !== true) return;\n const cutoff = props.deps.now() - days * 24 * 60 * 60 * 1000;\n const result = props.deps.withWrite?.(handle => pruneOlderThan(handle, cutoff));\n if (result === undefined) {\n props.ctx.ui.toast.show({\n message: \"the recorder is writing: try again in a moment\",\n variant: \"warning\"\n });\n } else {\n props.ctx.ui.toast.show({\n message: `pruned ${result.steps} steps · ${result.prompts} prompts · ${result.events} compactions`,\n variant: \"success\"\n });\n }\n setAnchor(undefined);\n setTick(value => value + 1);\n }\n\n /** Give the space a prune freed back to the filesystem. */\n async function compact() {\n if (sessionRunning()) {\n props.ctx.ui.toast.show({\n message: \"the session is running: compact again when it is idle\",\n variant: \"warning\"\n });\n return;\n }\n const confirmed = await props.ctx.ui.dialog.confirm({\n title: \"Compact the database\",\n message: \"SQLite rewrites the file to reclaim the space of deleted rows. It needs the recorder to be idle, and it can take a moment.\",\n label: {\n confirm: \"Compact\",\n cancel: \"Cancel\"\n }\n });\n if (confirmed !== true) return;\n const path = props.deps.dbPath();\n let result;\n try {\n result = props.deps.withWrite?.(handle => vacuum(handle, path));\n } catch {\n result = undefined;\n }\n if (result === undefined) {\n setBusy(true);\n props.ctx.ui.toast.show({\n message: \"the recorder is writing: try again in a moment\",\n variant: \"warning\"\n });\n } else {\n setBusy(false);\n props.ctx.ui.toast.show({\n message: `compacted: ${(result.freed / 1_048_576).toFixed(1)} MB back`,\n variant: \"success\"\n });\n }\n setTick(value => value + 1);\n }\n function back() {\n if (detail() === undefined) return;\n setDetail(undefined);\n setAnchor(undefined);\n }\n async function pickFilter(kind) {\n const db = props.deps.db();\n if (db === undefined) return;\n const all = filterOptions(db);\n const list = kind === \"model\" ? all.models : all.providers;\n const current = settings();\n const selected = kind === \"model\" ? current.modelID : current.providerID;\n const value = await props.ctx.ui.dialog.select({\n title: kind === \"model\" ? \"Filter by model\" : \"Filter by provider\",\n current: selected,\n options: [{\n title: `any · ${list.length} observed`,\n value: \"\"\n }, ...list.map(entry => ({\n title: entry.value,\n value: entry.value,\n description: `${entry.steps} steps`,\n category: kind\n }))]\n });\n if (value === undefined) return;\n props.update(draft => {\n if (kind === \"model\") draft.modelID = value;else draft.providerID = value;\n });\n setAnchor(undefined);\n setDetail(undefined);\n }\n const command = (id, title, bind, run) => ({\n id,\n title,\n bind,\n run\n });\n props.ctx.keymap.layer(() => {\n return {\n commands: [command(\"usage-insights.cursor.up\", \"Move up\", \"up\", () => move(-1)), command(\"usage-insights.cursor.down\", \"Move down\", \"down\", () => move(1)), command(\"usage-insights.cursor.up.alt\", \"Move up\", \"k\", () => move(-1)), command(\"usage-insights.cursor.down.alt\", \"Move down\", \"j\", () => move(1)), command(\"usage-insights.page.up\", \"Page up\", \"pageup\", () => move(-10)), command(\"usage-insights.page.down\", \"Page down\", \"pagedown\", () => move(10)), command(\"usage-insights.first\", \"First row\", \"home\", () => moveEdge(false)), command(\"usage-insights.last\", \"Last row\", \"end\", () => moveEdge(true)), command(\"usage-insights.drill\", \"Open the selected row\", \"return\", () => drill()), command(\"usage-insights.back\", \"Go back\", \"backspace\", () => back()), command(\"usage-insights.view.next\", \"Next view\", \"]\", () => {\n setHelp(false);\n props.update(draft => {\n draft.view = nextIn(PANEL_VIEWS, sanitizeSettings(draft).view, 1);\n });\n }), command(\"usage-insights.view.previous\", \"Previous view\", \"[\", () => {\n setHelp(false);\n props.update(draft => {\n draft.view = nextIn(PANEL_VIEWS, sanitizeSettings(draft).view, -1);\n });\n }), command(\"usage-insights.range.next\", \"Next range\", \"t\", () => props.update(draft => {\n draft.range = nextIn(RANGE_KEYS, sanitizeSettings(draft).range, 1);\n })), command(\"usage-insights.range.previous\", \"Previous range\", \"shift+t\", () => props.update(draft => {\n draft.range = nextIn(RANGE_KEYS, sanitizeSettings(draft).range, -1);\n })), command(\"usage-insights.sort.cycle\", \"Cycle sort column\", \"s\", () => props.update(draft => {\n const state = sanitizeSettings(draft);\n draft.sort = nextSort(state.view, state.sort === \"\" ? defaultSort(state.view) : state.sort, 1);\n })), command(\"usage-insights.sort.reverse\", \"Reverse sort\", \"shift+s\", () => props.update(draft => {\n draft.sortDesc = !sanitizeSettings(draft).sortDesc;\n })), command(\"usage-insights.bucket\", \"Next period bucket\", \"b\", () => props.update(draft => {\n draft.granularity = nextIn(GRANULARITIES, sanitizeSettings(draft).granularity, 1);\n draft.view = \"periods\";\n })), command(\"usage-insights.scope\", \"Toggle the turns scope\", \"a\", () => props.update(draft => {\n draft.turnsScope = sanitizeSettings(draft).turnsScope === \"session\" ? \"all\" : \"session\";\n draft.view = \"turns\";\n })), command(\"usage-insights.filter.model\", \"Filter by model\", \"m\", () => void pickFilter(\"model\")), command(\"usage-insights.filter.provider\", \"Filter by provider\", \"p\", () => void pickFilter(\"provider\")), command(\"usage-insights.filter.clear\", \"Clear filters\", \"x\", () => {\n props.update(draft => {\n draft.modelID = \"\";\n draft.providerID = \"\";\n });\n setAnchor(undefined);\n }), command(\"usage-insights.refresh\", \"Refresh\", \"r\", () => setTick(value => value + 1)), command(\"usage-insights.vacuum\", \"Compact the database\", \"v\", () => void compact()), command(\"usage-insights.help\", \"Help\", \"?\", () => setHelp(open => !open)), command(\"usage-insights.fullscreen\", \"Toggle full screen\", \"f\", () => props.input.toggleFullscreen()), command(\"usage-insights.close\", \"Close usage panel\", \"escape\", () => props.input.close()), ...PANEL_VIEWS.map((name, index) => command(`usage-insights.view.${name}`, `View ${name}`, String((index + 1) % 10), () => {\n setHelp(false);\n props.update(draft => {\n draft.view = name;\n });\n }))]\n };\n });\n const color = tone => {\n switch (tone) {\n case \"muted\":\n return props.ctx.theme.text.muted;\n case \"warning\":\n return props.ctx.theme.text.feedback.warning.base;\n case \"accent\":\n return props.ctx.theme.text.feedback.info.base;\n default:\n return props.ctx.theme.text.base;\n }\n };\n\n // The host paints the panel surface with `background.raised.base` (and\n // `background.base` full screen), so the cursor has to step up a rung: a\n // `raised.base` band would be invisible on the panel it sits on. `raised.high`\n // is the token the host itself uses for a selected row on a raised surface.\n const selectedBackground = () => props.ctx.theme.background.raised.high;\n const cursorIndex = () => cursor();\n return (() => {\n var _el$ = _$createElement(\"box\"),\n _el$2 = _$createElement(\"scrollbox\"),\n _el$3 = _$createElement(\"text\"),\n _el$4 = _$createElement(\"text\");\n _$insertNode(_el$, _el$2);\n _$setProp(_el$, \"flexDirection\", \"column\");\n _$setProp(_el$, \"paddingLeft\", 1);\n _$setProp(_el$, \"paddingRight\", 1);\n _$insertNode(_el$2, _el$3);\n _$insertNode(_el$2, _el$4);\n _$use(target => {\n scroller = target;\n // The scrollbox must not take focus: the layer above owns the keys,\n // and a focused scrollbox would swallow the arrows first.\n const focusable = target;\n if (focusable !== null && typeof focusable === \"object\") focusable.focusable = false;\n }, _el$2);\n _$setProp(_el$2, \"flexGrow\", 1);\n _$setProp(_el$2, \"scrollY\", true);\n _$setProp(_el$2, \"verticalScrollbarOptions\", {\n visible: true\n });\n _$insert(_el$2, _$createComponent(For, {\n get each() {\n return header();\n },\n children: line => (() => {\n var _el$5 = _$createElement(\"text\");\n _$insert(_el$5, () => line.text);\n _$effect(_$p => _$setProp(_el$5, \"fg\", color(line.tone), _$p));\n return _el$5;\n })()\n }), _el$3);\n _$insert(_el$2, _$createComponent(For, {\n get each() {\n return content();\n },\n children: (line, index) => {\n const selected = () => index() === selectedLineIndex(content(), cursorIndex());\n return (() => {\n var _el$6 = _$createElement(\"text\");\n _$insert(_el$6, () => line.text);\n _$effect(_p$ => {\n var _v$3 = `line-${index()}`,\n _v$4 = color(line.tone),\n _v$5 = selected() ? selectedBackground() : undefined,\n _v$6 = selected() ? TextAttributes.BOLD : undefined;\n _v$3 !== _p$.e && (_p$.e = _$setProp(_el$6, \"id\", _v$3, _p$.e));\n _v$4 !== _p$.t && (_p$.t = _$setProp(_el$6, \"fg\", _v$4, _p$.t));\n _v$5 !== _p$.a && (_p$.a = _$setProp(_el$6, \"bg\", _v$5, _p$.a));\n _v$6 !== _p$.o && (_p$.o = _$setProp(_el$6, \"attributes\", _v$6, _p$.o));\n return _p$;\n }, {\n e: undefined,\n t: undefined,\n a: undefined,\n o: undefined\n });\n return _el$6;\n })();\n }\n }), _el$3);\n _$insert(_el$4, () => keysFor(view()));\n _$effect(_p$ => {\n var _v$ = props.ctx.theme.text.muted,\n _v$2 = props.ctx.theme.text.muted;\n _v$ !== _p$.e && (_p$.e = _$setProp(_el$3, \"fg\", _v$, _p$.e));\n _v$2 !== _p$.t && (_p$.t = _$setProp(_el$4, \"fg\", _v$2, _p$.t));\n return _p$;\n }, {\n e: undefined,\n t: undefined\n });\n return _el$;\n })();\n}\n\n/** Line index under the cursor, so the renderer can highlight exactly one row. */\nfunction selectedLineIndex(lines, cursor) {\n let seen = -1;\n for (let index = 0; index < lines.length; index += 1) {\n if (lines[index].row === undefined) continue;\n seen += 1;\n if (seen === cursor) return index;\n }\n return -1;\n}",
12
+ "/**\n * The analysis layer: the reports the panel and the CLI show.\n *\n * Two shapes, chosen by measurement rather than taste:\n *\n * 1. **Aggregates in SQLite** — totals, counts, distinct values. `coverageOf`\n * and the filter pickers never move a row into JavaScript.\n * 2. **Narrow rows plus arithmetic in JS** — percentiles, pair rules, calendar\n * buckets. SQLite's window functions cost about 17 µs per row per report\n * here (a sort plus a materialisation), while reading the four to twelve\n * columns a report actually needs costs 1–4 µs per row. The percentiles and\n * the pair comparisons are therefore computed over lean projections: on the\n * audit's 120k-step database a whole view costs a few hundred milliseconds,\n * and on a normal one it is instant.\n *\n * The rules are not restated here. Cache reuse, idle gaps and the noise\n * threshold come from `core.ts` — the same functions the live line uses — so\n * the panel and the line can never disagree about what a cache loss is.\n * `test/reports.test.ts` holds every report to those rules.\n */\n\nimport type { Database } from \"bun:sqlite\"\nimport { cacheDrop, stepGap, tokensPerSecond, type AssistantMessage } from \"../core\"\nimport {\n buildWhere,\n DEFAULT_TTL_HINT_MS,\n latencyStats,\n modelKey,\n modelLabel,\n percentile,\n type Filters,\n type LatencyStats,\n type Range,\n type Usage,\n} from \"./queries\"\n\n/** The step's own clock, the expression the `steps_at` index covers. */\nexport const STEP_AT = \"COALESCE(created_at, recorded_at)\"\n\ntype Options = { range?: Range | undefined; filters?: Filters | undefined }\n\n// ---------------------------------------------------------------------------\n// Lean projections\n// ---------------------------------------------------------------------------\n\n/**\n * Turn `null` into `undefined` in place, so a row costs one object and not two.\n * Every query aliases its columns to the field names they are read through;\n * only the optional ones have to be normalized.\n */\nfunction normalize<Row>(rows: Row[], optional: readonly string[]): Row[] {\n for (const row of rows as unknown as Array<Record<string, unknown>>) {\n for (const key of optional) if (row[key] === null) row[key] = undefined\n }\n return rows\n}\n\nfunction groupBy<Value, Key>(items: readonly Value[], key: (item: Value) => Key): Map<Key, Value[]> {\n const groups = new Map<Key, Value[]>()\n for (const item of items) {\n const group = key(item)\n const list = groups.get(group)\n if (list !== undefined) list.push(item)\n else groups.set(group, [item])\n }\n return groups\n}\n\n/** One recorded step, as the reports read it. */\nexport type StepRow = {\n messageID: string\n sessionID: string\n providerID: string | undefined\n modelID: string | undefined\n variant: string | undefined\n input: number\n cached: number\n cacheWrite: number\n output: number\n reasoning: number\n cost: number\n createdAt: number | undefined\n streamedAt: number | undefined\n completedAt: number | undefined\n /** `COALESCE(created_at, recorded_at)`: the ordering time of the step. */\n at: number\n /** Set on the single step loaded before the range, as a reference. */\n boundary?: boolean | undefined\n}\n\nconst STEP_COLUMNS = `message_id AS messageID, session_id AS sessionID, provider_id AS providerID,\n model_id AS modelID, variant,\n COALESCE(input_tokens, 0) AS input, COALESCE(cache_read, 0) AS cached, COALESCE(cache_write, 0) AS cacheWrite,\n COALESCE(output_tokens, 0) AS output, COALESCE(reasoning_tokens, 0) AS reasoning, COALESCE(cost, 0) AS cost,\n created_at AS createdAt, streamed_at AS streamedAt, completed_at AS completedAt, ${STEP_AT} AS at`\n\nconst STEP_OPTIONAL = [\"providerID\", \"modelID\", \"variant\", \"createdAt\", \"streamedAt\", \"completedAt\"] as const\n\n/**\n * A step as the pair reports read it. Their projection leaves the generated\n * tokens out (nothing they compute sums them), and the type says so: an\n * aggregate that reads `output` cannot be handed a pair row by mistake.\n */\nexport type PairRow = Omit<StepRow, \"output\" | \"reasoning\">\n\n/** The pair reports read no generated tokens: `output` and `reasoning` stay out. */\nconst PAIR_COLUMNS = STEP_COLUMNS.replace(\"COALESCE(output_tokens, 0) AS output, COALESCE(reasoning_tokens, 0) AS reasoning, \", \"\")\n\n/** The steps of the range, narrow. Ordering is only paid for where it is read. */\nexport function stepRows(db: Database, options?: Options): StepRow[] {\n const filter = buildWhere(options?.range, options?.filters)\n const rows = db.query(`SELECT ${STEP_COLUMNS} FROM steps ${filter.where}`).all(...filter.params) as Array<Record<string, unknown>>\n return normalize(rows, STEP_OPTIONAL) as StepRow[]\n}\n\n/**\n * The steps of the range plus, for every session with a step inside it, the one\n * step before the range: the pair reports compare a step with the one that\n * preceded it, so the first step of a range has to see it.\n *\n * The boundary is one seek per session on the `(session_id, time)` index — a\n * few milliseconds for a far wider history — never the whole history of the\n * sessions in range.\n */\nexport function cachePairs(db: Database, options?: Options): PairRow[] {\n const filter = buildWhere(options?.range, options?.filters)\n const rows = normalize(\n db\n .query(`SELECT ${PAIR_COLUMNS} FROM steps ${filter.where} ORDER BY session_id, ${STEP_AT}, message_id`)\n .all(...filter.params) as Array<Record<string, unknown>>,\n STEP_OPTIONAL,\n ) as PairRow[]\n const from = options?.range?.from\n if (from === undefined || rows.length === 0) return rows\n const seek = db.query(\n `SELECT ${PAIR_COLUMNS} FROM steps\n WHERE session_id = ? AND COALESCE(created_at, recorded_at) < ?\n ORDER BY COALESCE(created_at, recorded_at) DESC, message_id DESC LIMIT 1`,\n )\n for (const sessionID of new Set(rows.map((row) => row.sessionID))) {\n const boundary = seek.get(sessionID, from) as Record<string, unknown> | null\n if (boundary === null) continue\n const step = normalize([boundary], STEP_OPTIONAL)[0] as PairRow\n step.boundary = true\n rows.push(step)\n }\n rows.sort((left, right) => {\n if (left.sessionID !== right.sessionID) return left.sessionID < right.sessionID ? -1 : 1\n if (left.at !== right.at) return left.at - right.at\n return left.messageID < right.messageID ? -1 : left.messageID > right.messageID ? 1 : 0\n })\n return rows\n}\n\n/** The same step in the message shape the shared pair rules are written for. */\nfunction message(step: PairRow): AssistantMessage {\n return {\n type: \"assistant\",\n id: step.messageID,\n ...(step.modelID === undefined\n ? {}\n : { model: { providerID: step.providerID ?? \"\", id: step.modelID, ...(step.variant === undefined ? {} : { variant: step.variant }) } }),\n tokens: { cache: { read: step.cached } },\n time: {\n ...(step.createdAt === undefined ? {} : { created: step.createdAt }),\n ...(step.streamedAt === undefined ? {} : { streamed: step.streamedAt }),\n ...(step.completedAt === undefined ? {} : { completed: step.completedAt }),\n },\n }\n}\n\n/** Consecutive steps of the same session, in order. */\nfunction pairsOf(rows: readonly PairRow[]): Array<{ previous: PairRow; current: PairRow }> {\n const pairs: Array<{ previous: PairRow; current: PairRow }> = []\n for (let index = 1; index < rows.length; index += 1) {\n const previous = rows[index - 1]!\n const current = rows[index]!\n if (previous.sessionID !== current.sessionID) continue\n pairs.push({ previous, current })\n }\n return pairs\n}\n\ntype Counted = { input: number; cached: number; cacheWrite: number; output: number; reasoning: number; cost: number }\n\n/** The buckets the panel and the line agree on. */\nfunction usageOf(steps: readonly Counted[]): Usage {\n const usage: Usage = { steps: 0, input: 0, cached: 0, cacheWrite: 0, output: 0, reasoning: 0, cost: 0 }\n for (const step of steps) {\n usage.steps += 1\n usage.input += step.input\n usage.cached += step.cached\n usage.cacheWrite += step.cacheWrite\n usage.output += step.output\n usage.reasoning += step.reasoning\n usage.cost += step.cost\n }\n return usage\n}\n\n// ---------------------------------------------------------------------------\n// Coverage\n// ---------------------------------------------------------------------------\n\nexport type CoverageReport = {\n steps: number\n models: number\n /** Providers the steps ran through. A step whose provider is unknown is counted nowhere. */\n providers: number\n missingModel: number\n unsettled: number\n missingTps: number\n firstAt: number | undefined\n lastAt: number | undefined\n}\n\n/**\n * What the panel cannot answer honestly: how many steps lack a model, and how\n * many never settled. A zero in the switch counts means \"never switched\" only\n * when this coverage is complete.\n */\nexport function coverageOf(db: Database, options?: Options): CoverageReport {\n const filter = buildWhere(options?.range, options?.filters)\n const row = db\n .query(\n `SELECT COUNT(*) AS steps,\n COUNT(DISTINCT CASE WHEN model_id IS NOT NULL THEN model_id || '#' || COALESCE(variant, '') END) AS models,\n COUNT(DISTINCT provider_id) AS providers,\n COALESCE(SUM(CASE WHEN model_id IS NULL THEN 1 ELSE 0 END), 0) AS missingModel,\n COALESCE(SUM(CASE WHEN completed_at IS NOT NULL OR finish IS NOT NULL THEN 0 ELSE 1 END), 0) AS unsettled,\n COALESCE(SUM(CASE WHEN (output_tokens + reasoning_tokens) > 0 AND created_at IS NOT NULL AND streamed_at IS NOT NULL AND streamed_at > created_at THEN 0 ELSE 1 END), 0) AS missingTps,\n MIN(${STEP_AT}) AS firstAt,\n MAX(${STEP_AT}) AS lastAt\n FROM steps ${filter.where}`,\n )\n .get(...filter.params) as {\n steps: number\n models: number\n providers: number\n missingModel: number\n unsettled: number\n missingTps: number\n firstAt: number | null\n lastAt: number | null\n }\n return {\n steps: row.steps,\n models: row.models,\n providers: row.providers,\n missingModel: row.missingModel,\n unsettled: row.unsettled,\n missingTps: row.missingTps,\n firstAt: row.firstAt ?? undefined,\n lastAt: row.lastAt ?? undefined,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Latency\n// ---------------------------------------------------------------------------\n\n/** The steps a latency report is built from. It reads no generated tokens. */\ntype GroupedRow = StepRow\n\nfunction groupedRows(db: Database, options?: Options): GroupedRow[] {\n const filter = buildWhere(options?.range, options?.filters)\n const rows = db.query(`SELECT ${STEP_COLUMNS} FROM steps ${filter.where}`).all(...filter.params) as Array<Record<string, unknown>>\n return normalize(rows, STEP_OPTIONAL) as StepRow[]\n}\n\n/** The three measures a latency report is made of. */\nfunction timingOf(step: StepRow): { wall: number | undefined; stream: number | undefined; tps: number | undefined } {\n const wall = step.createdAt !== undefined && step.completedAt !== undefined ? step.completedAt - step.createdAt : undefined\n const stream =\n step.createdAt !== undefined && step.streamedAt !== undefined && step.streamedAt > step.createdAt ? step.streamedAt - step.createdAt : undefined\n return { wall, stream, tps: tokensPerSecond(step.output + step.reasoning, step.createdAt, step.streamedAt) }\n}\n\nexport type LatencyReport = {\n wall: LatencyStats\n stream: LatencyStats\n tps: LatencyStats\n}\n\n/** The timings of the range: one read, shared by the overall report and the groups. */\nexport function timingRows(db: Database, options?: Options): GroupedRow[] {\n return groupedRows(db, options)\n}\n\n/** Overall wall time, streaming window and throughput percentiles. */\nexport function latencyOf(rows: readonly StepRow[]): LatencyReport {\n const wall: number[] = []\n const stream: number[] = []\n const tps: number[] = []\n for (const step of rows) {\n const timing = timingOf(step)\n if (timing.wall !== undefined) wall.push(timing.wall)\n if (timing.stream !== undefined) stream.push(timing.stream)\n if (timing.tps !== undefined) tps.push(timing.tps)\n }\n return { wall: latencyStats(wall), stream: latencyStats(stream), tps: latencyStats(tps) }\n}\n\nexport type LatencyGroupRow = {\n key: string\n label: string\n count: number\n usage: Usage\n wall: LatencyStats\n stream: LatencyStats\n tps: LatencyStats\n}\n\nexport type Dimension = \"model\" | \"provider\"\n\n/** Wall, streaming window and throughput percentiles per group. */\nexport function latencyGroupsOf(rows: readonly StepRow[], by: Dimension): LatencyGroupRow[] {\n const key = (step: StepRow) => (by === \"model\" ? modelKey(step.modelID ?? \"(unknown)\", step.variant) : step.providerID ?? \"(unknown)\")\n const groups: LatencyGroupRow[] = []\n for (const [group, steps] of groupBy(rows, key)) {\n const wall: number[] = []\n const stream: number[] = []\n const tps: number[] = []\n for (const step of steps) {\n const timing = timingOf(step)\n if (timing.wall !== undefined) wall.push(timing.wall)\n if (timing.stream !== undefined) stream.push(timing.stream)\n if (timing.tps !== undefined) tps.push(timing.tps)\n }\n const [modelID, variant] = group.split(\"#\")\n groups.push({\n key: group,\n label: by === \"model\" ? (variant === undefined || variant === \"\" ? modelID! : `${modelID} ${variant}`) : group,\n count: steps.length,\n usage: usageOf(steps),\n wall: latencyStats(wall),\n stream: latencyStats(stream),\n tps: latencyStats(tps),\n })\n }\n return groups.sort((left, right) => right.count - left.count || left.key.localeCompare(right.key))\n}\n\n// ---------------------------------------------------------------------------\n// Cache reuse\n// ---------------------------------------------------------------------------\n\nexport type CacheStats = {\n steps: number\n input: number\n cached: number\n cacheWrite: number\n /** The whole prompt the provider saw: `cached + input + cache_write`. */\n prompt: number\n /** Token-weighted reuse over the whole selection. */\n hit: number | undefined\n /** Median of the per-step reuse ratios: a few huge steps cannot hide a split pattern. */\n hitP50: number | undefined\n}\n\nconst EMPTY_CACHE: CacheStats = { steps: 0, input: 0, cached: 0, cacheWrite: 0, prompt: 0, hit: undefined, hitP50: undefined }\n\n/** Reuse of the prompt cache over a set of steps. */\nfunction cacheStatsOfSteps(steps: readonly { input: number; cached: number; cacheWrite: number }[]): CacheStats {\n let input = 0\n let cached = 0\n let cacheWrite = 0\n const ratios: number[] = []\n for (const step of steps) {\n input += step.input\n cached += step.cached\n cacheWrite += step.cacheWrite\n const prompt = step.input + step.cached + step.cacheWrite\n if (prompt > 0) ratios.push(step.cached / prompt)\n }\n const prompt = input + cached + cacheWrite\n return { steps: steps.length, input, cached, cacheWrite, prompt, hit: prompt > 0 ? cached / prompt : undefined, hitP50: percentile(ratios, 50) }\n}\n\n/** Reuse of the prompt cache over the range, as one row. */\nexport function cacheStatsOf(db: Database, options?: Options): CacheStats {\n const rows = stepRows(db, options)\n return rows.length === 0 ? EMPTY_CACHE : cacheStatsOfSteps(rows)\n}\n\nexport type CacheModelRow = {\n modelID: string\n variant: string | undefined\n cache: CacheStats\n}\n\n/**\n * Reuse of the prompt cache per model, most cached first, over the steps of the\n * range. A boundary step is not part of the range, so it is left out.\n */\nexport function cacheStatsByModel(rows: readonly PairRow[]): CacheModelRow[] {\n const out: CacheModelRow[] = []\n const inRange = rows.filter((row) => row.boundary !== true)\n for (const [, group] of groupBy(inRange, (row) => modelKey(row.modelID ?? \"(unknown)\", row.variant))) {\n out.push({ modelID: group[0]!.modelID ?? \"(unknown)\", variant: group[0]!.variant, cache: cacheStatsOfSteps(group) })\n }\n return out.sort((left, right) => right.cache.cached - left.cache.cached || left.modelID.localeCompare(right.modelID))\n}\n\n// ---------------------------------------------------------------------------\n// Pairs: switches, cache survival, TTL, losses\n// ---------------------------------------------------------------------------\n\nexport type SwitchRow = {\n modelID: string\n variant: string | undefined\n steps: number\n pairs: number\n /** Steps whose model, variant or provider differed from the step before. */\n changes: number\n /** Of those, the ones whose model (or variant) differed. */\n modelChanges: number\n /** Of those, the ones whose provider differed. */\n providerChanges: number\n /** Cache the changes cost: only a provider switch can forfeit a reusable cache. */\n cacheLost: number\n cacheLostEvents: number\n}\n\nexport type SwitchReport = {\n rows: SwitchRow[]\n totals: { pairs: number; changes: number; cacheLost: number; cacheLostEvents: number }\n}\n\n/**\n * Switches between consecutive steps of a session, by the two dimensions that\n * decide which cache holds the work: the model (with its variant) and the\n * provider.\n *\n * A switch is not by itself a fault — it is usually the user choosing another\n * model — so the report only names a cost where one is measurable: a provider\n * switch that leaves the model intact forfeits the cache the previous provider\n * held. A model switch has no measurable cost by definition, because two models\n * never share a cache to lose.\n */\nexport function switchesOf(rows: readonly PairRow[]): SwitchReport {\n const perModel = new Map<string, SwitchRow>()\n const totals = { pairs: 0, changes: 0, cacheLost: 0, cacheLostEvents: 0 }\n\n for (const { previous, current } of pairsOf(rows)) {\n if (previous.modelID === undefined || current.modelID === undefined) continue\n const key = modelKey(current.modelID, current.variant)\n const row = perModel.get(key) ?? {\n modelID: current.modelID,\n variant: current.variant,\n steps: 0,\n pairs: 0,\n changes: 0,\n modelChanges: 0,\n providerChanges: 0,\n cacheLost: 0,\n cacheLostEvents: 0,\n }\n row.steps += 1\n row.pairs += 1\n totals.pairs += 1\n\n const modelChanged = key !== modelKey(previous.modelID, previous.variant)\n const providerChanged = previous.providerID !== undefined && current.providerID !== undefined && previous.providerID !== current.providerID\n if (modelChanged) row.modelChanges += 1\n if (providerChanged) row.providerChanges += 1\n if (modelChanged || providerChanged) {\n row.changes += 1\n totals.changes += 1\n // The scope is the model alone: a provider switch forfeits the cache the\n // previous provider held, and that is the cost this report names.\n const lost = cacheDrop(message(previous), message(current), \"model\")\n if (lost !== undefined) {\n row.cacheLost += lost\n row.cacheLostEvents += 1\n totals.cacheLost += lost\n totals.cacheLostEvents += 1\n }\n }\n perModel.set(key, row)\n }\n\n return {\n rows: [...perModel.values()].sort((left, right) => right.changes - left.changes || right.steps - left.steps || left.modelID.localeCompare(right.modelID)),\n totals,\n }\n}\n\nexport type GapBucket = {\n label: string\n samples: number\n /** Pairs that still reused the prompt cache. */\n retained: number\n drops: number\n lostTokens: number\n retainedRatio: number | undefined\n}\n\nexport type ProviderReuseRow = {\n /** The provider, or `(unknown)` when a step ran without one. */\n provider: string\n samples: number\n retained: number\n drops: number\n lostTokens: number\n retainedRatio: number | undefined\n}\n\nexport type GapReport = {\n buckets: GapBucket[]\n providers: ProviderReuseRow[]\n samples: number\n}\n\n/** Idle-gap buckets, coarse enough to read at a glance. */\nexport const GAP_BUCKETS: ReadonlyArray<readonly [string, number, number | undefined]> = [\n [\"<1m\", 0, 60_000],\n [\"1–5m\", 60_000, 5 * 60_000],\n [\"5–15m\", 5 * 60_000, 15 * 60_000],\n [\"15–60m\", 15 * 60_000, 60 * 60_000],\n [\"1–6h\", 60 * 60_000, 6 * 60 * 60_000],\n [\">6h\", 6 * 60 * 60_000, undefined],\n]\n\nfunction bucketFor(gapMs: number): number {\n const index = GAP_BUCKETS.findIndex(([, from, to]) => gapMs >= from && (to === undefined || gapMs < to))\n return index === -1 ? GAP_BUCKETS.length - 1 : index\n}\n\n/**\n * How long an idle gap the prompt cache survives, per gap bucket and per\n * provider. Only pairs of the same model are comparable, which is exactly the\n * set the cache rule already uses.\n */\nexport function gapReportOf(rows: readonly PairRow[]): GapReport {\n const buckets: GapBucket[] = GAP_BUCKETS.map(([label]) => ({ label, samples: 0, retained: 0, drops: 0, lostTokens: 0, retainedRatio: undefined }))\n const providers = new Map<string, ProviderReuseRow>()\n let samples = 0\n\n for (const { previous, current } of pairsOf(rows)) {\n if (previous.modelID === undefined || current.modelID === undefined) continue\n if (modelKey(previous.modelID, previous.variant) !== modelKey(current.modelID, current.variant)) continue\n // A pair that switched provider is not a survival sample: the switch\n // tally owns that case, and a switch is not an idle gap.\n if ((previous.providerID ?? \"\") !== (current.providerID ?? \"\")) continue\n const gap = stepGap(message(previous), message(current))\n if (gap === undefined) continue\n const lost = cacheDrop(message(previous), message(current))\n const bucket = buckets[bucketFor(gap)]!\n bucket.samples += 1\n samples += 1\n if (lost === undefined) bucket.retained += 1\n else {\n bucket.drops += 1\n bucket.lostTokens += lost\n }\n\n const provider = current.providerID ?? previous.providerID ?? \"(unknown)\"\n const row = providers.get(provider) ?? { provider, samples: 0, retained: 0, drops: 0, lostTokens: 0, retainedRatio: undefined }\n row.samples += 1\n if (lost === undefined) row.retained += 1\n else {\n row.drops += 1\n row.lostTokens += lost\n }\n providers.set(provider, row)\n }\n\n for (const bucket of buckets) bucket.retainedRatio = bucket.samples > 0 ? bucket.retained / bucket.samples : undefined\n for (const row of providers.values()) row.retainedRatio = row.samples > 0 ? row.retained / row.samples : undefined\n return {\n buckets,\n providers: [...providers.values()].sort((left, right) => right.samples - left.samples || left.provider.localeCompare(right.provider)),\n samples,\n }\n}\n\nexport type TtlEvidenceRow = {\n providerID: string\n modelID: string\n samples: number\n /** Largest idle gap that still hit the cache. */\n maxHitGapMs: number | undefined\n /** Smallest idle gap after which reuse was lost. */\n minDropGapMs: number | undefined\n}\n\n/**\n * Empirical bounds on the provider cache TTL: the longest idle gap that still\n * reused the cache, and the shortest gap that lost it. Comparing the two per\n * provider turns TTL guesses into observations.\n */\nexport function ttlEvidenceOf(rows: readonly PairRow[]): TtlEvidenceRow[] {\n const evidence = new Map<string, TtlEvidenceRow>()\n\n for (const { previous, current } of pairsOf(rows)) {\n if (previous.modelID === undefined || current.modelID === undefined) continue\n const key = `${current.providerID ?? \"\"}/${current.modelID}`\n const before = `${previous.providerID ?? \"\"}/${previous.modelID}#${previous.variant ?? \"\"}`\n const after = `${current.providerID ?? \"\"}/${current.modelID}#${current.variant ?? \"\"}`\n if (before !== after) continue\n\n const row = evidence.get(key) ?? {\n providerID: current.providerID ?? \"(unknown)\",\n modelID: current.modelID,\n samples: 0,\n maxHitGapMs: undefined,\n minDropGapMs: undefined,\n }\n row.samples += 1\n const gapMs = stepGap(message(previous), message(current))\n if (gapMs !== undefined) {\n const lost = cacheDrop(message(previous), message(current))\n if (lost === undefined) row.maxHitGapMs = row.maxHitGapMs === undefined ? gapMs : Math.max(row.maxHitGapMs, gapMs)\n else row.minDropGapMs = row.minDropGapMs === undefined ? gapMs : Math.min(row.minDropGapMs, gapMs)\n }\n evidence.set(key, row)\n }\n return [...evidence.values()].sort(\n (left, right) => right.samples - left.samples || `${left.providerID}/${left.modelID}`.localeCompare(`${right.providerID}/${right.modelID}`),\n )\n}\n\nexport type DropReason = \"compaction\" | \"expired\" | \"unknown\"\n\nexport type CacheDropRow = {\n sessionID: string\n messageID: string\n providerID: string | undefined\n modelID: string | undefined\n variant: string | undefined\n at: number\n tokens: number\n gapMs: number | undefined\n prevCached: number\n reason: DropReason\n}\n\n/**\n * Cache reuse losses with a best-effort reason: a compaction in between, an\n * idle gap long enough to be provider-side expiry, or unknown.\n *\n * Compactions come from the `events` table: one query for the sessions in\n * play, never one per loss.\n */\nexport function cacheDropsOf(db: Database, rows: readonly PairRow[], options?: { ttlHintMs?: number }): CacheDropRow[] {\n const ttlHint = options?.ttlHintMs ?? DEFAULT_TTL_HINT_MS\n const losses: Array<{ previous: PairRow; current: PairRow; tokens: number; gapMs: number | undefined }> = []\n for (const { previous, current } of pairsOf(rows)) {\n // The model scope: a provider switch forfeits the cache the previous\n // provider held, which is a loss like any other.\n const tokens = cacheDrop(message(previous), message(current), \"model\")\n if (tokens === undefined) continue\n losses.push({ previous, current, tokens, gapMs: stepGap(message(previous), message(current)) })\n }\n if (losses.length === 0) return []\n\n const compactions = new Map<string, number[]>()\n const sessionIDs = [...new Set(losses.map((loss) => loss.current.sessionID))]\n const events = db\n .query(\n `SELECT session_id, at FROM events\n WHERE kind = 'compaction' AND at IS NOT NULL AND session_id IN (${sessionIDs.map(() => \"?\").join(\", \")})\n ORDER BY at`,\n )\n .all(...sessionIDs) as { session_id: string; at: number }[]\n for (const event of events) {\n const list = compactions.get(event.session_id)\n if (list !== undefined) list.push(event.at)\n else compactions.set(event.session_id, [event.at])\n }\n\n return losses\n .map((loss) => {\n const ended = loss.previous.completedAt ?? loss.previous.streamedAt ?? loss.previous.createdAt\n const compacted = (compactions.get(loss.current.sessionID) ?? []).some((time) => ended !== undefined && time >= ended && time <= loss.current.at)\n const reason: DropReason = compacted ? \"compaction\" : loss.gapMs !== undefined && loss.gapMs >= ttlHint ? \"expired\" : \"unknown\"\n return {\n sessionID: loss.current.sessionID,\n messageID: loss.current.messageID,\n providerID: loss.current.providerID,\n modelID: loss.current.modelID,\n variant: loss.current.variant,\n at: loss.current.at,\n tokens: loss.tokens,\n gapMs: loss.gapMs,\n prevCached: loss.previous.cached,\n reason,\n }\n })\n .sort((left, right) => left.at - right.at)\n}\n\n// ---------------------------------------------------------------------------\n// Providers\n// ---------------------------------------------------------------------------\n\nexport type ProviderSummary = {\n /** The provider, or undefined for steps recorded without one. */\n provider: string | undefined\n /** Models this provider served. */\n models: string[]\n share: number\n usage: Usage\n cache: CacheStats\n tps: number | undefined\n wall: number | undefined\n firstAt: number | undefined\n lastAt: number | undefined\n}\n\nexport type ProviderRow = ProviderSummary & {\n modelID: string\n variant: string | undefined\n}\n\n/** The numbers of one group of steps. */\nfunction groupValues(steps: readonly StepRow[]): { cache: CacheStats; tps: number | undefined; wall: number | undefined } {\n const wall: number[] = []\n const tps: number[] = []\n for (const step of steps) {\n const timing = timingOf(step)\n if (timing.wall !== undefined) wall.push(timing.wall)\n if (timing.tps !== undefined) tps.push(timing.tps)\n }\n return { cache: cacheStatsOfSteps(steps), tps: latencyStats(tps).p50, wall: latencyStats(wall).p50 }\n}\n\nfunction providerOf(steps: readonly StepRow[], models: string): Omit<ProviderSummary, \"provider\"> & { provider: string | undefined } {\n const values = groupValues(steps)\n const times = steps.map((step) => step.at)\n return {\n provider: steps[0]!.providerID,\n models: models.split(\"\\u0000\"),\n share: 0,\n usage: usageOf(steps),\n cache: values.cache,\n tps: values.tps,\n wall: values.wall,\n firstAt: times.length > 0 ? Math.min(...times) : undefined,\n lastAt: times.length > 0 ? Math.max(...times) : undefined,\n }\n}\n\nconst modelNames = (steps: readonly StepRow[]): string =>\n [...new Set(steps.map((step) => modelKey(step.modelID ?? \"(unknown)\", step.variant)))].map((key) => modelLabel(key.slice(0, key.indexOf(\"#\")), key.slice(key.indexOf(\"#\") + 1) || undefined)).join(\"\\u0000\")\n\n/** One row per provider, over every model it served. */\nexport function providerSummaryOf(rows: readonly StepRow[]): ProviderSummary[] {\n const out: ProviderSummary[] = []\n for (const steps of groupBy(rows, (step) => step.providerID ?? \"\").values()) {\n const summary = providerOf(steps, modelNames(steps))\n out.push({ ...summary, share: rows.length === 0 ? 0 : steps.length / rows.length })\n }\n return out.sort((left, right) => right.usage.steps - left.usage.steps || (left.provider ?? \"\").localeCompare(right.provider ?? \"\"))\n}\n\n/** One row per model × provider: through which provider each model ran, and how much. */\nexport function providerBreakdownOf(rows: readonly StepRow[]): ProviderRow[] {\n const out: ProviderRow[] = []\n for (const [model, modelRows] of groupBy(rows, (step) => modelKey(step.modelID ?? \"(unknown)\", step.variant))) {\n for (const steps of groupBy(modelRows, (step) => step.providerID ?? \"\").values()) {\n const [modelID, variant] = [model.slice(0, model.indexOf(\"#\")), model.slice(model.indexOf(\"#\") + 1)]\n out.push({\n // One row per model, so its `models` field names that one model — in the\n // same label form the provider summary uses.\n ...providerOf(steps, modelNames(steps)),\n modelID,\n variant: variant === \"\" ? undefined : variant,\n share: steps.length / modelRows.length,\n })\n }\n }\n return out.sort((left, right) => right.usage.steps - left.usage.steps || right.cache.cached - left.cache.cached)\n}\n\n// ---------------------------------------------------------------------------\n// Calendar periods\n// ---------------------------------------------------------------------------\n\nexport type PeriodGranularity = \"day\" | \"week\" | \"month\"\n\nexport type PeriodRow = {\n key: string\n label: string\n /** Start of the bucket, local time. */\n at: number\n steps: number\n sessions: number\n usage: Usage\n cache: CacheStats\n}\n\n/** ISO-8601 week number and its Monday, in local time. */\nexport function isoWeek(date: Date): { year: number; week: number; monday: Date } {\n const monday = new Date(date.getFullYear(), date.getMonth(), date.getDate())\n monday.setDate(monday.getDate() - ((monday.getDay() + 6) % 7))\n const thursday = new Date(monday.getFullYear(), monday.getMonth(), monday.getDate() + 3)\n const firstThursday = new Date(thursday.getFullYear(), 0, 4)\n firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3)\n const week = 1 + Math.round((thursday.getTime() - firstThursday.getTime()) / (7 * 24 * 60 * 60 * 1000))\n return { year: thursday.getFullYear(), week, monday }\n}\n\n/** Key, label and local start of the period `at` falls in. */\nexport function periodStart(at: number, granularity: PeriodGranularity): { key: string; label: string; start: number } {\n const date = new Date(at)\n const pad = (value: number) => String(value).padStart(2, \"0\")\n if (granularity === \"month\") {\n const start = new Date(date.getFullYear(), date.getMonth(), 1)\n return { key: `${start.getFullYear()}-${pad(start.getMonth() + 1)}`, label: `${start.getFullYear()}-${pad(start.getMonth() + 1)}`, start: start.getTime() }\n }\n if (granularity === \"week\") {\n const { year, week, monday } = isoWeek(date)\n return { key: `${year}-W${pad(week)}`, label: `${year}-W${pad(week)}`, start: monday.getTime() }\n }\n const start = new Date(date.getFullYear(), date.getMonth(), date.getDate())\n const key = `${start.getFullYear()}-${pad(start.getMonth() + 1)}-${pad(start.getDate())}`\n return { key, label: key, start: start.getTime() }\n}\n\n/** Totals per local calendar day, week or month, oldest first. */\nexport function periodsOf(rows: readonly StepRow[], granularity: PeriodGranularity): PeriodRow[] {\n const out: PeriodRow[] = []\n for (const [key, steps] of groupBy(rows, (step) => periodStart(step.at, granularity).key)) {\n const period = periodStart(steps[0]!.at, granularity)\n out.push({\n key,\n label: period.label,\n at: period.start,\n steps: steps.length,\n sessions: new Set(steps.map((step) => step.sessionID)).size,\n usage: usageOf(steps),\n cache: cacheStatsOfSteps(steps),\n })\n }\n return out.sort((left, right) => left.at - right.at)\n}\n",
13
+ "import type { Database } from \"bun:sqlite\"\nimport { formatTokens } from \"./core\"\nimport {\n cacheDropsOf,\n cachePairs,\n cacheStatsByModel,\n cacheStatsOf,\n coverageOf,\n providerBreakdownOf,\n providerSummaryOf,\n gapReportOf,\n latencyGroupsOf,\n latencyOf,\n periodsOf,\n stepRows,\n switchesOf,\n timingRows,\n ttlEvidenceOf,\n type CacheStats,\n type ProviderRow,\n type PairRow,\n type ProviderSummary,\n type Dimension,\n type LatencyGroupRow,\n type SwitchRow,\n} from \"./stats/aggregates\"\nimport { childSessionIDs, modelKey, models, overview, sessionUsageOf, sessions, STEP_TIME, turns, type Filters } from \"./stats/queries\"\nimport type { DbInfo } from \"./stats/maintenance\"\nimport {\n cacheLines,\n dbLines,\n formatCount,\n latencyLines,\n modelsLines,\n overviewLines,\n periodLines,\n providerSummaryLines,\n providerLines,\n sessionsLines,\n turnsLines,\n type CacheModelRow,\n type Granularity,\n type LineFormatOptions,\n type PanelLine,\n type PanelRow,\n type PanelView,\n type RangeKey,\n} from \"./panel-data\"\n\n/**\n * The panel's data assembly: read the database, run the queries the active view\n * needs and hand plain lines to the renderer. Kept out of the component so\n * every view can be asserted from an in-memory database, exactly as the CLI\n * report renders it.\n */\n\nexport type TurnsScope = \"session\" | \"all\"\n\n/** Everything the panel shows, in one request object. */\nexport type PanelRequest = {\n db: Database\n view: PanelView\n range: RangeKey\n filters: Filters\n granularity: Granularity\n turnsScope: TurnsScope\n sort: string\n sortDesc: boolean\n sessionID: string\n sessionStart: number | undefined\n now: number\n format?: LineFormatOptions | undefined\n /** Drill-down target: when set, the panel shows this detail instead. */\n detail?: PanelRow | undefined\n /** The database view's own state: where the file is and what it holds. */\n database?: DbInfo | undefined\n /** Set when the last vacuum could not take the write lock. */\n busy?: boolean | undefined\n width: number\n}\n\n/** Sorting options per view; the first is the default. */\nexport const SORT_KEYS: Partial<Record<PanelView, readonly string[]>> = {\n models: [\"steps\", \"cached\", \"hit\", \"out\", \"cost\"],\n providers: [\"steps\", \"cached\", \"hit\", \"cost\", \"share\", \"tps\"],\n latency: [\"n\", \"tps\", \"wall\"],\n sessions: [\"last\", \"steps\", \"cached\", \"cost\"],\n turns: [\"time\", \"cost\", \"tokens\"],\n}\n\nexport function sortKeys(view: PanelView): readonly string[] {\n return SORT_KEYS[view] ?? []\n}\n\nexport function defaultSort(view: PanelView): string {\n return sortKeys(view)[0] ?? \"\"\n}\n\nexport function nextSort(view: PanelView, current: string, step: number): string {\n const keys = sortKeys(view)\n if (keys.length === 0) return current\n const index = keys.indexOf(current)\n if (index === -1) return keys[0]!\n return keys[(index + step + keys.length) % keys.length]!\n}\n\nexport function detailLabel(row: PanelRow): string {\n switch (row.kind) {\n case \"model\":\n return `model ${row.variant !== undefined ? `${row.value} ${row.variant}` : row.value}`\n case \"session\":\n case \"turn\":\n return `session ${row.value}`\n case \"period\":\n return `period ${row.value}`\n case \"provider\":\n return `provider ${row.value}`\n case \"prune\":\n return `prune older than ${row.value} days`\n }\n}\n\n/** Build the panel lines for the request. Never throws: a failure is a line. */\nexport function buildPanel(request: PanelRequest): PanelLine[] {\n try {\n return build(request)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n return [{ text: `stats query failed: ${message}`, tone: \"warning\" }]\n }\n}\n\nfunction build(request: PanelRequest): PanelLine[] {\n const { db, view, now, sessionStart, width } = request\n // The `session` range is the session itself, not \"everything recorded after it\n // started\": siblings and subagents run in that window too.\n const scoped = request.range === \"session\" && request.sessionID !== \"\"\n const range = scoped ? undefined : request.range === \"session\" ? (sessionStart === undefined ? undefined : { from: sessionStart }) : rangeForRequest(request.range, now)\n const filters: Filters = scoped ? { ...request.filters, sessionID: request.sessionID } : request.filters\n if (request.detail !== undefined) return buildDetail(request, range, filters)\n\n switch (view) {\n case \"overview\": {\n const usage = overview(db, range, filters)\n const sessionUsage =\n request.sessionID === \"\" ? undefined : overview(db, undefined, { ...request.filters, sessionID: request.sessionID })\n // Subagents are their own sessions; showing them apart explains why the\n // line's `sess` segment (one session) is smaller than the whole family.\n const childIDs = request.sessionID === \"\" ? [] : childSessionIDs(db, request.sessionID)\n const children = childIDs.length === 0 ? [] : sessionUsageOf(db, childIDs)\n return overviewLines(\n {\n usage,\n session: sessionUsage,\n ...(children.length > 0 ? { subagents: children } : {}),\n range: request.range,\n activity: activityOf(db, range, now),\n coverage: coverageOf(db, { range, filters }),\n },\n width,\n )\n }\n case \"models\": {\n const rows = sortModels(models(db, range, filters), request.sort, request.sortDesc)\n // One read of the pairs serves the reuse table and the switch counts.\n const pairs = cachePairs(db, { range, filters })\n const switches = switchesOf(pairs)\n return modelsLines(\n rows,\n width,\n {\n cache: cacheByModel(pairs),\n switches: switchesByModel(switches.rows),\n },\n 40,\n )\n }\n case \"providers\":\n return providerSummaryLines(sortProviders(providerSummaryOf(stepRows(db, { range, filters })), request.sort, request.sortDesc), width, 40)\n case \"cache\": {\n // One read of the pairs serves the losses, the TTL bounds and the\n // survival buckets: they are three views of the same comparison.\n const pairs = cachePairs(db, { range, filters })\n return cacheLines(\n {\n drops: cacheDropsOf(db, pairs),\n evidence: ttlEvidenceOf(pairs),\n gap: gapReportOf(pairs),\n models: topCacheModels(pairs),\n },\n width,\n )\n }\n case \"periods\":\n return periodLines(periodsOf(stepRows(db, { range, filters }), request.granularity), width, request.granularity, 40)\n case \"latency\": {\n // One read of the timings serves the overall block and the group table.\n const timings = timingRows(db, { range, filters })\n const by: Dimension = filters.modelID !== undefined ? \"provider\" : \"model\"\n return latencyLines(latencyOf(timings), width, sortLatency(latencyGroupsOf(timings, by), request.sort, request.sortDesc), 14)\n }\n case \"sessions\": {\n const rows = sortSessions(sessions(db, { range, filters, limit: 200 }), request.sort, request.sortDesc)\n const child = rows.filter((row) => row.parentID !== undefined && rows.some((parent) => parent.sessionID === row.parentID))\n return sessionsLines(rows, width, {\n limit: 20,\n rollup: { subagentSteps: child.reduce((sum, row) => sum + row.steps, 0), subagentCost: child.reduce((sum, row) => sum + row.cost, 0) },\n })\n }\n case \"db\":\n // The path and the file's size are the panel's, not the database's: a\n // builder without them says so instead of inventing a location.\n return request.database === undefined\n ? [{ text: \"the database location is unavailable to this panel\", tone: \"warning\" }]\n : dbLines({ info: request.database, busy: request.busy, now }, width)\n case \"turns\": {\n const sessionID = request.turnsScope === \"session\" ? request.sessionID : undefined\n const rows = sortTurns(turns(db, { sessionID, range, filters, limit: 300 }), request.sort, request.sortDesc)\n const titles =\n request.turnsScope === \"all\"\n ? new Map(sessions(db, { range, filters, limit: 200 }).map((row) => [row.sessionID, row.title ?? row.sessionID]))\n : undefined\n return turnsLines(rows.slice(0, 60), width, { ...request.format, sessionTitles: titles })\n }\n }\n}\n\nfunction rangeForRequest(key: RangeKey, now: number): { from?: number | undefined; to?: number | undefined } | undefined {\n switch (key) {\n case \"today\": {\n const start = new Date(now)\n start.setHours(0, 0, 0, 0)\n return { from: start.getTime() }\n }\n case \"7d\":\n return { from: now - 7 * 24 * 60 * 60 * 1000 }\n case \"30d\":\n return { from: now - 30 * 24 * 60 * 60 * 1000 }\n default:\n return undefined\n }\n}\n\n// ---------------------------------------------------------------------------\n// Detail screens\n// ---------------------------------------------------------------------------\n\nfunction buildDetail(\n request: PanelRequest,\n range: { from?: number | undefined; to?: number | undefined } | undefined,\n filters: Filters,\n): PanelLine[] {\n const row = request.detail!\n const db = request.db\n const width = request.width\n const head: PanelLine = { text: detailLabel(row), tone: \"accent\" }\n const back: PanelLine = { text: \"backspace returns · the header shows where you came from\", tone: \"muted\" }\n\n if (row.kind === \"model\") {\n // The identity is the model *and* its variant: the variant is a different\n // model for the provider, so it gets its own detail.\n const scoped: Filters = { ...filters, modelKey: modelKey(row.value, row.variant) }\n const stats = cacheStatsOf(db, { range, filters: scoped })\n // One read of the steps serves the provider breakdown and the latency.\n const steps = stepRows(db, { range, filters: scoped })\n return [\n head,\n {\n // The same buckets as the line and the views: `in` is uncached (fresh +\n // writes) and `prompt` is everything the provider saw.\n text: `steps ${stats.steps} · in ${formatTokens(stats.prompt - stats.cached)} · prompt ${formatTokens(stats.prompt)} · cached ${formatTokens(\n stats.cached,\n )} · write ${formatTokens(stats.cacheWrite)} · hit ${percent(stats.hit)} · p50 ${percent(stats.hitP50)}`,\n tone: \"muted\",\n },\n { text: \"\", tone: \"muted\" },\n ...providerLines(providerBreakdownOf(steps), width, 40),\n { text: \"\", tone: \"muted\" },\n // One model, so its latency is worth reading per provider: that is what\n // the model's own identity no longer separates.\n ...latencyLines(latencyOf(steps), width, latencyGroupsOf(steps, \"provider\"), 14),\n { text: \"\", tone: \"muted\" },\n back,\n ]\n }\n if (row.kind === \"session\" || row.kind === \"turn\") {\n const rows = turns(db, { sessionID: row.value, limit: 80 })\n return [head, { text: \"\", tone: \"muted\" }, ...turnsLines(rows, width, request.format), { text: \"\", tone: \"muted\" }, back]\n }\n if (row.kind === \"provider\") {\n // A provider detail: every model it served, then its latency.\n const scoped: Filters = { ...filters, provider: row.value }\n // One read of the steps serves the reuse line and the latency block.\n const steps = stepRows(db, { range, filters: scoped })\n const stats = cacheStatsOf(db, { range, filters: scoped })\n return [\n head,\n {\n text: `steps ${stats.steps} · in ${formatTokens(stats.prompt - stats.cached)} · prompt ${formatTokens(stats.prompt)} · cached ${formatTokens(\n stats.cached,\n )} · hit ${percent(stats.hit)} · p50 ${percent(stats.hitP50)}`,\n tone: \"muted\",\n },\n { text: \"\", tone: \"muted\" },\n ...providerLines(providerBreakdownOf(steps), width, 40),\n { text: \"\", tone: \"muted\" },\n ...latencyLines(latencyOf(steps), width, latencyGroupsOf(steps, \"model\"), 14),\n { text: \"\", tone: \"muted\" },\n back,\n ]\n }\n if (row.kind === \"period\") {\n // The bucket is one row of the period report: the same query, one row kept.\n const periods = periodsOf(stepRows(db, { range, filters }), request.granularity)\n const period = periods.find((entry) => entry.key === row.value)\n return [\n head,\n { text: `${formatCount(period?.steps ?? 0)} steps`, tone: \"muted\" },\n { text: \"\", tone: \"muted\" },\n ...providerSummaryLines(providerSummaryOf(stepRows(db, { range, filters })), width, 40),\n { text: \"\", tone: \"muted\" },\n back,\n ]\n }\n return [head, back]\n}\n\nfunction percent(value: number | undefined): string {\n return value === undefined ? \"–\" : `${(value * 100).toFixed(1)}%`\n}\n\n// ---------------------------------------------------------------------------\n// Sorting\n// ---------------------------------------------------------------------------\n\nfunction sortModels(rows: ReturnType<typeof models>, sort: string, desc: boolean): ReturnType<typeof models> {\n const sign = desc ? -1 : 1\n const pick = (model: (typeof rows)[number]): number => {\n if (sort === \"cached\") return model.totals.cached\n if (sort === \"out\") return model.totals.output\n if (sort === \"cost\") return model.totals.cost\n return model.totals.steps\n }\n return [...rows].sort((left, right) => sign * (pick(right) - pick(left)))\n}\n\nfunction sortProviders(rows: readonly ProviderSummary[], sort: string, desc: boolean): ProviderSummary[] {\n const sign = desc ? -1 : 1\n const pick = (row: (typeof rows)[number]): number => {\n if (sort === \"cached\") return row.cache.cached\n if (sort === \"hit\") return row.cache.hit ?? -1\n if (sort === \"cost\") return row.usage.cost\n if (sort === \"share\") return row.share\n if (sort === \"tps\") return row.tps ?? -1\n return row.usage.steps\n }\n return [...rows].sort((left, right) => sign * (pick(right) - pick(left)))\n}\n\nfunction sortLatency(rows: readonly LatencyGroupRow[], sort: string, desc: boolean): LatencyGroupRow[] {\n const sign = desc ? -1 : 1\n const pick = (row: (typeof rows)[number]): number => {\n if (sort === \"tps\") return row.tps.p50 ?? -1\n if (sort === \"wall\") return row.wall.p50 ?? -1\n return row.count\n }\n return [...rows].sort((left, right) => sign * (pick(right) - pick(left)))\n}\n\nfunction sortSessions(rows: ReturnType<typeof sessions>, sort: string, desc: boolean): ReturnType<typeof sessions> {\n const sign = desc ? -1 : 1\n const pick = (row: (typeof rows)[number]): number => {\n if (sort === \"steps\") return row.steps\n if (sort === \"cached\") return row.cached\n if (sort === \"cost\") return row.cost\n return row.lastAt ?? 0\n }\n return [...rows].sort((left, right) => sign * (pick(right) - pick(left)))\n}\n\nfunction sortTurns(rows: ReturnType<typeof turns>, sort: string, desc: boolean): ReturnType<typeof turns> {\n const sign = desc ? -1 : 1\n const pick = (row: (typeof rows)[number]): number => {\n if (sort === \"cost\") return row.totals.cost\n if (sort === \"tokens\") return row.totals.cached + row.totals.input + row.totals.output\n return row.endAt ?? 0\n }\n return [...rows].sort((left, right) => sign * (pick(right) - pick(left)))\n}\n\n// ---------------------------------------------------------------------------\n// Small derived series\n// ---------------------------------------------------------------------------\n\n/** Token reuse per model, for the models view and the cache view. */\nexport function cacheByModel(pairs: readonly PairRow[]): Map<string, CacheStats> {\n const map = new Map<string, CacheStats>()\n for (const row of cacheStatsByModel(pairs)) {\n map.set(modelKey(row.modelID, row.variant), row.cache)\n }\n return map\n}\n\n/** The switch summary per `modelKey`, as the models view reads it. */\nexport function switchesByModel(rows: readonly SwitchRow[]): Map<string, SwitchRow> {\n return new Map(rows.map((row) => [modelKey(row.modelID, row.variant), row]))\n}\n\n/** The cache table of the cache view: the models holding the most cache. */\nfunction topCacheModels(pairs: readonly PairRow[]): CacheModelRow[] {\n return cacheStatsByModel(pairs)\n .slice(0, 10)\n .map((row) => ({ usage: { modelID: row.modelID, variant: row.variant }, cache: row.cache }))\n}\n\n/**\n * Steps per bucket for the overview sparkline. The bucket size is chosen so a\n * range reads as roughly 48 bars, and always from a fixed ladder so the axis is\n * stable between refreshes.\n */\nfunction activityOf(db: Database, range: { from?: number | undefined; to?: number | undefined } | undefined, now: number): number[] {\n // All time has no lower bound: span the recorded window instead of starting\n // at the epoch, or the sparkline would be one bar of data and 46 of nothing.\n const earliest = db.query(`SELECT MIN(${STEP_TIME}) AS at FROM steps`).get() as { at: number | null } | null\n const from = range?.from ?? earliest?.at ?? 0\n const to = range?.to ?? now\n const span = Math.max(1, to - from)\n const ladder = [3_600_000, 3 * 3_600_000, 6 * 3_600_000, 12 * 3_600_000, 86_400_000, 3 * 86_400_000, 7 * 86_400_000, 30 * 86_400_000]\n const bucket = ladder.find((size) => span / size <= 48) ?? ladder[ladder.length - 1]!\n const rows = db\n .query(\n `SELECT CAST(COALESCE(created_at, recorded_at) / ? AS INTEGER) AS bucket, COUNT(*) AS steps\n FROM steps\n WHERE COALESCE(created_at, recorded_at) >= ?\n GROUP BY bucket ORDER BY bucket`,\n )\n .all(bucket, from) as { bucket: number; steps: number }[]\n if (rows.length === 0) return []\n const first = Math.floor(from / bucket)\n const last = rows[rows.length - 1]!.bucket\n const series: number[] = []\n for (let index = first; index <= last; index += 1) {\n series.push(rows.find((row) => row.bucket === index)?.steps ?? 0)\n }\n return series\n}\n",
14
+ "import type { Database } from \"bun:sqlite\"\nimport { statSync } from \"node:fs\"\nimport { TABLES, type TableName } from \"./db\"\n\n/**\n * Database maintenance: what the file holds, how much of it is worth keeping,\n * and the two operations that give the space back.\n *\n * Everything is explicit — nothing here runs on its own. The panel's `db` view\n * and `bun run report` are the only callers.\n */\n\nexport type TableSize = {\n name: string\n rows: number\n}\n\nexport type DbInfo = {\n path: string\n /** Size of the database file, in bytes. */\n bytes: number\n /** Size of its write-ahead log, when one exists. */\n walBytes: number | undefined\n schemaVersion: number\n tables: TableSize[]\n /** Oldest and newest recorded step, the window the views can answer for. */\n firstAt: number | undefined\n lastAt: number | undefined\n /** Steps a prune at each cutoff would remove, for the panel to offer choices. */\n removable: Array<{ days: number; steps: number }>\n}\n\nconst STEP_TIME = \"COALESCE(created_at, recorded_at)\"\nconst DAY = 24 * 60 * 60 * 1000\n\nfunction size(path: string): number {\n try {\n return statSync(path).size\n } catch {\n return 0\n }\n}\n\nexport function dbInfo(db: Database, path: string, now = Date.now()): DbInfo {\n const tables: TableSize[] = TABLES.map((table) => ({\n name: table,\n rows: (db.query(`SELECT COUNT(*) AS n FROM ${table}`).get() as { n: number }).n,\n }))\n const window = db.query(`SELECT MIN(${STEP_TIME}) AS first, MAX(${STEP_TIME}) AS last FROM steps`).get() as {\n first: number | null\n last: number | null\n }\n const version = db.query(\"SELECT value FROM meta WHERE key = 'schema_version'\").get() as { value: string } | null\n return {\n path,\n bytes: size(path),\n walBytes: path === \":memory:\" ? undefined : size(`${path}-wal`) || undefined,\n schemaVersion: version === null ? 0 : Number.parseInt(version.value, 10),\n tables,\n firstAt: window.first ?? undefined,\n lastAt: window.last ?? undefined,\n removable: [30, 90, 365].map((days) => ({\n days,\n steps: (db.query(`SELECT COUNT(*) AS n FROM steps WHERE ${STEP_TIME} < ?`).get(now - days * DAY) as { n: number }).n,\n })),\n }\n}\n\nexport type PruneResult = {\n steps: number\n prompts: number\n events: number\n}\n\n/**\n * Rows deleted per transaction. A single delete of a long history holds the\n * write lock for the whole scan — measured at 1.3 s for 123k steps — and the\n * recorder writes on the agent's event path, so it would wait that long. In\n * batches the lock is taken and released per batch and the recorder\n * interleaves. Each batch is atomic; the prune as a whole is not, which is\n * fine for \"delete what is older than this\": a prune that is interrupted\n * leaves fewer rows deleted, and the next one finishes the job.\n */\nconst PRUNE_BATCH = 5_000\n\nfunction deleteBatched(db: Database, table: \"steps\" | \"prompts\" | \"events\", where: string, cutoff: number): number {\n // Table names and the WHERE clause are constants of this module: no value\n // from outside is ever interpolated into the statement.\n const statement = db.query(`DELETE FROM ${table} WHERE rowid IN (SELECT rowid FROM ${table} WHERE ${where} LIMIT ${PRUNE_BATCH})`)\n let removed = 0\n for (;;) {\n const changes = statement.run(cutoff).changes\n removed += changes\n if (changes < PRUNE_BATCH) break\n }\n return removed\n}\n\n/**\n * Delete recorded usage older than `cutoff`. Steps, their prompts and the\n * compaction events that explain their cache losses go together: a compaction\n * that no step references answers nothing.\n */\nexport function pruneOlderThan(db: Database, cutoff: number): PruneResult {\n return {\n steps: deleteBatched(db, \"steps\", `${STEP_TIME} < ?`, cutoff),\n prompts: deleteBatched(db, \"prompts\", \"created_at IS NOT NULL AND created_at < ?\", cutoff),\n events: deleteBatched(db, \"events\", \"at IS NOT NULL AND at < ?\", cutoff),\n }\n}\n\n/** Everything the database occupies: the main file plus its write-ahead log. */\nfunction footprint(path: string): number {\n return size(path) + size(`${path}-wal`)\n}\n\n/** Reclaim the space a prune freed. Returns the bytes the database lost. */\nexport function vacuum(db: Database, path: string): { before: number; after: number; freed: number } {\n const before = footprint(path)\n // A checkpoint first: with WAL the freed pages live in the log until the\n // database is written back into the main file.\n db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\")\n db.exec(\"VACUUM\")\n const after = footprint(path)\n return { before, after, freed: Math.max(0, before - after) }\n}\n\nexport type { TableName }\n",
15
+ "import { Database } from \"bun:sqlite\"\nimport { chmodSync, mkdirSync } from \"node:fs\"\nimport { dirname } from \"node:path\"\nimport { ensureRegularFile } from \"./paths\"\nimport { isToken, sanitizeLabel, TEXT_LIMIT } from \"../text\"\n\n/**\n * Persistent usage store. One SQLite database holds every recorded step,\n * prompt, event and session row — and nothing else: the recorder only writes\n * what a view reads.\n *\n * The database is opened in WAL mode with a busy timeout so the server\n * recorder process and the TUI reader process can share it safely. The two\n * sides open it differently on purpose:\n *\n * - `openStatsDb` writes: schema, migrations and recording (the server).\n * - `openStatsDbReadOnly` only reads (the TUI), so no view can change the file\n * and no reader pays for a schema write.\n * - `openStatsDbWrite` is a short-lived maintenance connection (prune, vacuum)\n * that gives up quickly instead of holding the recorder up.\n */\n\nexport const SCHEMA_VERSION = 9\n\n/** Longest title or path kept: a label on a line, not a document. */\nconst LABEL_LIMIT = 4 * TEXT_LIMIT\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS sessions (\n session_id TEXT PRIMARY KEY,\n parent_id TEXT,\n directory TEXT,\n title TEXT,\n started_at INTEGER,\n updated_at INTEGER\n);\n\nCREATE TABLE IF NOT EXISTS prompts (\n session_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n created_at INTEGER,\n PRIMARY KEY (session_id, message_id)\n);\nCREATE INDEX IF NOT EXISTS prompts_created ON prompts(created_at);\n\nCREATE TABLE IF NOT EXISTS steps (\n message_id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n parent_id TEXT,\n provider_id TEXT,\n model_id TEXT,\n variant TEXT,\n input_tokens INTEGER NOT NULL DEFAULT 0,\n output_tokens INTEGER NOT NULL DEFAULT 0,\n reasoning_tokens INTEGER NOT NULL DEFAULT 0,\n cache_read INTEGER NOT NULL DEFAULT 0,\n cache_write INTEGER NOT NULL DEFAULT 0,\n cost REAL NOT NULL DEFAULT 0,\n created_at INTEGER,\n streamed_at INTEGER,\n completed_at INTEGER,\n finish TEXT,\n recorded_at INTEGER NOT NULL\n);\nCREATE INDEX IF NOT EXISTS steps_session ON steps(session_id, created_at);\nCREATE INDEX IF NOT EXISTS steps_model ON steps(provider_id, model_id, variant);\nCREATE INDEX IF NOT EXISTS steps_completed ON steps(completed_at);\n-- Every range filter and every ordering compares the step's own clock, which\n-- falls back to the recording time for a step that was already in flight when\n-- the plugin loaded. The index has to cover that expression, or every ranged\n-- view is a full table scan.\nCREATE INDEX IF NOT EXISTS steps_at ON steps(COALESCE(created_at, recorded_at));\nCREATE INDEX IF NOT EXISTS steps_session_at ON steps(session_id, COALESCE(created_at, recorded_at));\n\nCREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n kind TEXT NOT NULL,\n message_id TEXT,\n at INTEGER,\n data TEXT\n);\nCREATE INDEX IF NOT EXISTS events_session ON events(session_id, at);\nCREATE INDEX IF NOT EXISTS events_kind ON events(kind, at);\n`\n\n/** Tables carried by `exportNdjson` / `importNdjson`, in import-safe order. */\nexport const TABLES = [\"sessions\", \"prompts\", \"events\", \"steps\"] as const\n\nexport type TableName = (typeof TABLES)[number]\n\nexport type StepRecord = {\n messageID: string\n sessionID: string\n parentID?: string | undefined\n /** The provider the model ran through, as OpenCode configured it (`opencode-go`). */\n providerID?: string | undefined\n modelID?: string | undefined\n variant?: string | undefined\n input?: number | undefined\n output?: number | undefined\n reasoning?: number | undefined\n cacheRead?: number | undefined\n cacheWrite?: number | undefined\n cost?: number | undefined\n createdAt?: number | undefined\n streamedAt?: number | undefined\n completedAt?: number | undefined\n finish?: string | undefined\n /** Clock of the recording itself, defaults to `Date.now()`. */\n recordedAt?: number | undefined\n}\n\nexport type PromptRecord = {\n sessionID: string\n messageID: string\n createdAt?: number | undefined\n}\n\nexport type EventRecord = {\n sessionID: string\n kind: string\n messageID?: string | undefined\n at?: number | undefined\n data?: unknown\n}\n\nexport type SessionRecord = {\n sessionID: string\n parentID?: string | undefined\n directory?: string | undefined\n title?: string | undefined\n startedAt?: number | undefined\n updatedAt?: number | undefined\n}\n\nfunction jsonText(value: unknown): string | null {\n if (value === undefined || value === null) return null\n if (typeof value === \"string\") return value\n try {\n return JSON.stringify(value)\n } catch {\n return null\n }\n}\n\n/**\n * Columns and tables the recorder used to write and no view ever read. They are\n * dropped from an existing database, together with the rows of the events that\n * followed the same fate, so a long-lived file shrinks instead of carrying dead\n * weight forever.\n *\n * `router` is here because the gateway that named the routing endpoint in a\n * response header (`x-opencode-endpoint-id`) stopped naming one: the identity a\n * step is grouped by is the provider the model ran through, and the endpoint a\n * gateway happened to route to is no longer observable at all.\n */\nconst REMOVED_COLUMNS = [\"provider_state\", \"upstream\", \"upstream_source\", \"upstream_model\", \"tps\", \"router\"] as const\nconst REMOVED_EVENT_KINDS = [\"execution\", \"step-failed\", \"model-selected\", \"usage-recorded\"] as const\n\nfunction dropUnused(db: Database): void {\n const objects = db.query(\"SELECT name, type FROM sqlite_master WHERE type IN ('table', 'index')\").all() as {\n name: string\n type: string\n }[]\n const tables = new Set(objects.filter((row) => row.type === \"table\").map((row) => row.name))\n const indexes = new Set(objects.filter((row) => row.type === \"index\").map((row) => row.name))\n\n if (tables.has(\"http_responses\")) db.exec(\"DROP TABLE http_responses\")\n // These indexes read a column that is about to go: SQLite refuses to drop an\n // indexed column, so the index goes first.\n if (indexes.has(\"steps_upstream\")) db.exec(\"DROP INDEX steps_upstream\")\n if (indexes.has(\"steps_router\")) db.exec(\"DROP INDEX steps_router\")\n\n const columns = new Set((db.query(\"PRAGMA table_info(steps)\").all() as { name: string }[]).map((column) => column.name))\n for (const name of REMOVED_COLUMNS) {\n if (columns.has(name)) db.exec(`ALTER TABLE steps DROP COLUMN ${name}`)\n }\n\n if (tables.has(\"events\")) {\n const kinds = REMOVED_EVENT_KINDS.map((kind) => `'${kind}'`).join(\", \")\n db.exec(`DELETE FROM events WHERE kind IN (${kinds})`)\n }\n}\n\n/**\n * Bring an existing file up to date. There is no version ladder: a column the\n * schema carries is created with the table, and a column the recorder stopped\n * writing is dropped by `dropUnused`, which is idempotent and also heals a\n * database a half-applied run left behind.\n */\nfunction migrate(db: Database): void {\n dropUnused(db)\n}\n\n/**\n * The file holds session titles, project directories and usage history, so it\n * is created for the user alone. Windows takes its access from the parent\n * directory's ACL, which is not something a plugin should rewrite.\n */\nconst POSIX = process.platform !== \"win32\"\n\nfunction hardenDirectory(path: string): void {\n if (!POSIX) return\n try {\n chmodSync(path, 0o700)\n } catch {\n // A shared or read-only directory: the database is still usable.\n }\n}\n\nfunction hardenFiles(path: string): void {\n if (!POSIX || path === \":memory:\") return\n for (const file of [path, `${path}-wal`, `${path}-shm`]) {\n try {\n chmodSync(file, 0o600)\n } catch {\n // The WAL and the shared-memory file are created on the first write and\n // removed on the last close, so one of them is often simply absent.\n }\n }\n}\n\n/** Directory for a database path, created private on POSIX. */\nfunction prepareDirectory(path: string): void {\n if (path === \":memory:\") return\n mkdirSync(dirname(path), { recursive: true, mode: 0o700 })\n hardenDirectory(dirname(path))\n}\n\n/** Open (creating when needed) the stats database and apply the schema. */\nexport function openStatsDb(path: string): Database {\n if (path !== \":memory:\") ensureRegularFile(path)\n prepareDirectory(path)\n const db = new Database(path, { create: true })\n db.exec(\"PRAGMA journal_mode = WAL\")\n db.exec(\"PRAGMA busy_timeout = 5000\")\n db.exec(\"PRAGMA synchronous = NORMAL\")\n db.exec(SCHEMA)\n migrate(db)\n db.query(\"INSERT INTO meta (key, value) VALUES ('schema_version', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value\").run(\n String(SCHEMA_VERSION),\n )\n // The schema write is what creates the WAL and its shared-memory file.\n hardenFiles(path)\n return db\n}\n\n/**\n * Reader connection for the TUI: no schema, no migration, no write. It throws\n * when the database is absent or was never written by the recorder, so the\n * caller can say so instead of showing an empty panel.\n */\nexport function openStatsDbReadOnly(path: string): Database {\n ensureRegularFile(path)\n const db = new Database(path, { readonly: true })\n try {\n db.query(\"SELECT 1 FROM steps LIMIT 1\").get()\n } catch (error) {\n db.close()\n throw error\n }\n return db\n}\n\n/**\n * Short-lived write connection for maintenance. The busy timeout is short on\n * purpose: a prune or a vacuum that cannot take the lock must fail in front of\n * the user, not hold the recorder's response path for seconds.\n */\nexport function openStatsDbWrite(path: string): Database {\n if (path !== \":memory:\") ensureRegularFile(path)\n prepareDirectory(path)\n const db = new Database(path, { create: true })\n db.exec(\"PRAGMA busy_timeout = 250\")\n db.exec(\"PRAGMA synchronous = NORMAL\")\n hardenFiles(path)\n return db\n}\n\n/**\n * Insert or refresh an assistant step. Idempotent by message ID so replayed\n * events, restarts and NDJSON imports never duplicate usage. A non-null field\n * (the provider, the model, the finish reason) survives a re-record with `NULL`\n * through COALESCE.\n *\n * The provider is the identity every view groups by, so it is validated as a\n * token before it is stored: a value that is not a provider id is recorded as\n * absent instead of becoming a grouping key.\n */\nexport function recordStep(db: Database, record: StepRecord): void {\n db.query(\n `INSERT INTO steps (\n message_id, session_id, parent_id, provider_id, model_id, variant,\n input_tokens, output_tokens, reasoning_tokens, cache_read, cache_write, cost,\n created_at, streamed_at, completed_at, finish, recorded_at\n ) VALUES (\n $messageID, $sessionID, $parentID, $providerID, $modelID, $variant,\n $input, $output, $reasoning, $cacheRead, $cacheWrite, $cost,\n $createdAt, $streamedAt, $completedAt, $finish, $recordedAt\n )\n ON CONFLICT(message_id) DO UPDATE SET\n session_id = excluded.session_id,\n parent_id = COALESCE(excluded.parent_id, steps.parent_id),\n provider_id = COALESCE(excluded.provider_id, steps.provider_id),\n model_id = COALESCE(excluded.model_id, steps.model_id),\n variant = COALESCE(excluded.variant, steps.variant),\n input_tokens = excluded.input_tokens,\n output_tokens = excluded.output_tokens,\n reasoning_tokens = excluded.reasoning_tokens,\n cache_read = excluded.cache_read,\n cache_write = excluded.cache_write,\n cost = excluded.cost,\n created_at = COALESCE(excluded.created_at, steps.created_at),\n streamed_at = COALESCE(excluded.streamed_at, steps.streamed_at),\n completed_at = COALESCE(excluded.completed_at, steps.completed_at),\n finish = COALESCE(excluded.finish, steps.finish),\n recorded_at = excluded.recorded_at`,\n ).run({\n $messageID: record.messageID,\n $sessionID: record.sessionID,\n $parentID: record.parentID ?? null,\n $providerID: record.providerID !== undefined && isToken(record.providerID) ? record.providerID : null,\n $modelID: record.modelID ?? null,\n $variant: record.variant ?? null,\n $input: record.input ?? 0,\n $output: record.output ?? 0,\n $reasoning: record.reasoning ?? 0,\n $cacheRead: record.cacheRead ?? 0,\n $cacheWrite: record.cacheWrite ?? 0,\n $cost: record.cost ?? 0,\n $createdAt: record.createdAt ?? null,\n $streamedAt: record.streamedAt ?? null,\n $completedAt: record.completedAt ?? null,\n $finish: record.finish === undefined ? null : sanitizeLabel(record.finish, 64),\n $recordedAt: record.recordedAt ?? Date.now(),\n })\n}\n\nexport function recordPrompt(db: Database, record: PromptRecord): void {\n db.query(\n `INSERT INTO prompts (session_id, message_id, created_at)\n VALUES ($sessionID, $messageID, $createdAt)\n ON CONFLICT(session_id, message_id) DO UPDATE SET created_at = COALESCE(excluded.created_at, prompts.created_at)`,\n ).run({\n $sessionID: record.sessionID,\n $messageID: record.messageID,\n $createdAt: record.createdAt ?? null,\n })\n}\n\nexport function recordEvent(db: Database, record: EventRecord): void {\n db.query(\n `INSERT INTO events (session_id, kind, message_id, at, data)\n VALUES ($sessionID, $kind, $messageID, $at, $data)`,\n ).run({\n $sessionID: record.sessionID,\n $kind: sanitizeLabel(record.kind, 64),\n $messageID: record.messageID ?? null,\n $at: record.at ?? Date.now(),\n $data: jsonText(record.data),\n })\n}\n\nexport function upsertSession(db: Database, record: SessionRecord): void {\n db.query(\n `INSERT INTO sessions (session_id, parent_id, directory, title, started_at, updated_at)\n VALUES ($sessionID, $parentID, $directory, $title, $startedAt, $updatedAt)\n ON CONFLICT(session_id) DO UPDATE SET\n parent_id = COALESCE(excluded.parent_id, sessions.parent_id),\n directory = COALESCE(excluded.directory, sessions.directory),\n title = COALESCE(excluded.title, sessions.title),\n started_at = COALESCE(sessions.started_at, excluded.started_at),\n updated_at = COALESCE(excluded.updated_at, sessions.updated_at)`,\n ).run({\n $sessionID: record.sessionID,\n $parentID: record.parentID ?? null,\n // Titles are written by a model and directories come from the host: both\n // are displayed, so neither may carry control characters into a terminal.\n $directory: record.directory === undefined ? null : sanitizeLabel(record.directory, LABEL_LIMIT),\n $title: record.title === undefined ? null : sanitizeLabel(record.title, LABEL_LIMIT),\n $startedAt: record.startedAt ?? null,\n $updatedAt: record.updatedAt ?? null,\n })\n}\n\n/** Dump every table as NDJSON (`{\"table\": \"...\", \"row\": {...}}` per line). */\nexport function exportNdjson(db: Database): string {\n const lines: string[] = []\n for (const table of TABLES) {\n const rows = db.query(`SELECT * FROM ${table}`).all() as Record<string, unknown>[]\n for (const row of rows) lines.push(JSON.stringify({ table, row }))\n }\n return lines.length > 0 ? `${lines.join(\"\\n\")}\\n` : \"\"\n}\n\n/** Column shape an import has to respect: the type and the NOT NULL rules. */\ntype ImportColumn = {\n name: string\n /** Declared affinity: `INTEGER`, `REAL`, `TEXT`, `BLOB` or empty. */\n type: string\n notNull: boolean\n primaryKey: boolean\n /** A column with a default may be left out of the import entirely. */\n hasDefault: boolean\n}\n\nfunction numericAffinity(type: string): boolean {\n const upper = type.toUpperCase()\n return upper.includes(\"INT\") || upper.includes(\"REAL\") || upper.includes(\"NUM\") || upper.includes(\"DEC\") || upper.includes(\"DOUB\")\n}\n\n/**\n * One imported value, forced into the shape of its column. Text is sanitized\n * and capped like everything else that is displayed; a number that is not a\n * finite number becomes NULL, so a crafted dump cannot store `NaN` or a\n * string where a view does arithmetic.\n */\nfunction importValue(value: unknown, column: ImportColumn): string | number | null {\n if (value === null || value === undefined) return null\n if (numericAffinity(column.type)) {\n const parsed = typeof value === \"number\" ? value : Number(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null\n if (typeof value === \"boolean\") return value ? 1 : 0\n if (typeof value === \"object\") return sanitizeLabel(JSON.stringify(value) ?? \"\", LABEL_LIMIT)\n return sanitizeLabel(String(value), LABEL_LIMIT)\n}\n\n/**\n * Load an `exportNdjson` dump back in. Rows are inserted with `OR IGNORE`, so\n * importing the same dump twice is harmless. Unknown tables and rows that are\n * not objects are rejected; a row that cannot fill a required column is\n * skipped rather than aborting the rest of the import. A column the row does\n * not carry is left to its default, the same way a hand-written insert is.\n */\nexport function importNdjson(db: Database, text: string): number {\n const columns = new Map<TableName, ImportColumn[]>()\n for (const table of TABLES) {\n const info = db.query(`PRAGMA table_info(${table})`).all() as {\n name: string\n type: string\n notnull: number\n pk: number\n dflt_value: string | null\n }[]\n columns.set(\n table,\n info.map((column) => ({\n name: column.name,\n type: column.type,\n notNull: column.notnull === 1,\n primaryKey: column.pk > 0,\n hasDefault: column.dflt_value !== null,\n })),\n )\n }\n\n let imported = 0\n for (const line of text.split(\"\\n\")) {\n const trimmed = line.trim()\n if (!trimmed) continue\n let parsed: { table?: string; row?: Record<string, unknown> }\n try {\n parsed = JSON.parse(trimmed) as { table?: string; row?: Record<string, unknown> }\n } catch {\n continue\n }\n const table = parsed.table as TableName | undefined\n const row = parsed.row\n if (!table || !row || !(TABLES as readonly string[]).includes(table)) continue\n const shape = columns.get(table) ?? []\n if (shape.length === 0) continue\n const names: string[] = []\n const values: (string | number)[] = []\n let importable = true\n for (const column of shape) {\n const value = importValue(row[column.name], column)\n if (value !== null) {\n names.push(column.name)\n values.push(value)\n continue\n }\n // NULL for a column that cannot hold one has no default to fall back on,\n // and a NULL in a text primary key is a key the database cannot use; an\n // INTEGER PRIMARY KEY is a rowid, so leaving it out assigns one.\n const required = (column.notNull || (column.primaryKey && !numericAffinity(column.type))) && !column.hasDefault\n if (required) {\n importable = false\n break\n }\n }\n if (!importable) continue\n const placeholders = names.map(() => \"?\").join(\", \")\n db.query(`INSERT OR IGNORE INTO ${table} (${names.join(\", \")}) VALUES (${placeholders})`).run(...values)\n imported += 1\n }\n return imported\n}\n",
16
+ "import { homedir } from \"node:os\"\nimport { join } from \"node:path\"\nimport { statSync } from \"node:fs\"\n\n/**\n * SQLite database used when no `db` option is configured: the per-user state\n * directory of the current OS, never a shared temp directory.\n *\n * The subdirectory is the plugin's package name, never a generic one: the\n * `opencode` tree is shared with OpenCode itself and with every other plugin,\n * so a `stats/` of our own would be a collision waiting to happen.\n *\n * - Windows: `%LOCALAPPDATA%\\opencode\\opencode-usage-insights\\usage.db`\n * - macOS: `~/Library/Application Support/opencode/opencode-usage-insights/usage.db`\n * - Linux/other: `$XDG_STATE_HOME/opencode/opencode-usage-insights/usage.db`,\n * falling back to `~/.local/state/opencode/opencode-usage-insights/usage.db`\n *\n * Pure by design so every OS can be asserted from any machine.\n */\nexport function defaultDbPath(env: Record<string, string | undefined> = process.env, platform: string = process.platform): string {\n const name = join(\"opencode\", \"opencode-usage-insights\", \"usage.db\")\n if (platform === \"win32\") {\n return join(env.LOCALAPPDATA ?? join(homedir(), \"AppData\", \"Local\"), name)\n }\n if (platform === \"darwin\") {\n return join(homedir(), \"Library\", \"Application Support\", name)\n }\n return join(env.XDG_STATE_HOME ?? join(homedir(), \".local\", \"state\"), name)\n}\n\n/**\n * Expand a leading `~` (or `~/`), so a configured path reads the way every\n * shell does. Windows accepts the backslash form too.\n */\nexport function expandHome(path: string, home: string = homedir()): string {\n if (path === \"~\") return home\n if (path.startsWith(\"~/\") || path.startsWith(\"~\\\\\")) return join(home, path.slice(2))\n return path\n}\n\n/**\n * Resolve the effective database path: explicit plugin option first, then the\n * `OPENCODE_USAGE_INSIGHTS_DB` escape hatch (handy for dev and tests), then\n * the per-OS state directory. A leading `~` is expanded in all three.\n */\nexport function resolveDbPath(\n configured?: string | undefined,\n env: Record<string, string | undefined> = process.env,\n platform: string = process.platform,\n): string {\n const home = env.HOME ?? env.USERPROFILE ?? homedir()\n if (configured) return expandHome(configured, home)\n if (env.OPENCODE_USAGE_INSIGHTS_DB) return expandHome(env.OPENCODE_USAGE_INSIGHTS_DB, home)\n return defaultDbPath(env, platform)\n}\n\n/**\n * A configured path must not be a directory, a device or a FIFO: SQLite would\n * fail obscurely, or write where nobody expects it. Symlinks to a regular file\n * are followed (`statSync`, not `lstat`) because moving the state directory to\n * another disk is a legitimate setup. A missing file is fine — the plugin\n * creates it.\n */\nexport function ensureRegularFile(path: string): void {\n let stats\n try {\n stats = statSync(path)\n } catch {\n return\n }\n if (!stats.isFile()) throw new Error(`not a regular file: ${path}`)\n}\n",
17
+ "import { effect as _$effect } from \"@opentui/solid\";\nimport { insert as _$insert } from \"@opentui/solid\";\nimport { createComponent as _$createComponent } from \"@opentui/solid\";\nimport { setProp as _$setProp } from \"@opentui/solid\";\nimport { createElement as _$createElement } from \"@opentui/solid\";\n/** @jsxImportSource @opentui/solid */\n\nimport { For, createMemo } from \"solid-js\";\nimport { sidebarLines, sessionSummaryLines } from \"./panel-data\";\nimport { overview, turns } from \"./stats/queries\";\n\n/**\n * Sidebar history: the session's accumulated usage, then the last finished\n * turns — when and through which provider, the model when it changed, and the\n * prompt volumes. Reads the same database, the same buckets and the same\n * formatter as the panel and the live line, and refreshes whenever a turn ends.\n */\n\nexport function TurnsSidebar(props) {\n const color = tone => {\n switch (tone) {\n case \"muted\":\n return props.ctx.theme.text.muted;\n case \"warning\":\n return props.ctx.theme.text.feedback.warning.base;\n case \"accent\":\n return props.ctx.theme.text.feedback.info.base;\n default:\n return props.ctx.theme.text.base;\n }\n };\n const lines = createMemo(() => {\n props.deps.refresh();\n const db = props.deps.db();\n if (db === undefined) return [];\n const budget = props.deps.budget ?? 34;\n try {\n // The session's own steps, never the window after it started: siblings\n // and subagents are their own sessions.\n const summary = sessionSummaryLines(overview(db, undefined, {\n sessionID: props.sessionID\n }), budget);\n const history = sidebarLines(turns(db, {\n sessionID: props.sessionID,\n limit: props.deps.limit\n }), {\n ...props.options,\n budget\n });\n return [...summary, {\n text: \"turns\",\n tone: \"accent\"\n }, ...history];\n } catch {\n return [];\n }\n });\n return (() => {\n var _el$ = _$createElement(\"box\");\n _$setProp(_el$, \"flexDirection\", \"column\");\n _$setProp(_el$, \"paddingLeft\", 1);\n _$setProp(_el$, \"paddingRight\", 1);\n _$insert(_el$, _$createComponent(For, {\n get each() {\n return lines();\n },\n children: line => (() => {\n var _el$2 = _$createElement(\"text\");\n _$insert(_el$2, () => line.text);\n _$effect(_$p => _$setProp(_el$2, \"fg\", color(line.tone), _$p));\n return _el$2;\n })()\n }));\n return _el$;\n })();\n}"
18
+ ],
19
+ "mappings": "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",
20
+ "debugId": "6DDD20AEAE1B401D64756E2164756E21",
21
+ "names": []
22
+ }