tuneloop 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/registry.ts","../src/core/model.ts","../src/pricing/models.json","../src/pricing/openrouter.ts","../src/pricing/pricing.ts","../src/adapters/claude-code/index.ts","../src/util/walk.ts","../src/adapters/claude-code/parse.ts","../src/core/hash.ts","../src/adapters/claude-code/actions.ts","../src/adapters/codex/index.ts","../src/adapters/codex/parse.ts","../src/adapters/codex/actions.ts","../src/adapters/opencode/index.ts","../src/adapters/opencode/db.ts","../src/adapters/opencode/actions.ts","../src/adapters/opencode/parse.ts","../src/core/turns.ts","../src/core/blocks.ts","../src/processors/segment-blocks.ts","../src/processors/files-touched.ts","../src/processors/github-pr.ts","../src/processors/outcomes-git.ts","../src/processors/pr-content-match.ts","../src/processors/enrich-session.ts","../src/config.ts","../src/llm/providers.ts","../src/core/runner.ts","../src/llm/anthropic.ts","../src/llm/json.ts","../src/llm/openai.ts","../src/llm/index.ts","../src/store/store.ts","../src/store/apply-patch.ts","../src/core/error-category.ts","../src/core/facets.ts","../src/core/measures.ts","../src/commands/analyze.ts","../src/core/merge.ts","../src/store/db.ts","../src/util/log.ts","../src/util/progress.ts","../src/util/sh.ts","../src/server/http.ts","../src/commands/serve.ts","../src/query/run.ts"],"sourcesContent":["import type { SourceAdapter } from '../adapters/types'\nimport type { Processor } from './processor'\n\n/**\n * The two extension points. Adapters turn a vendor transcript into the\n * normalized model; processors derive facts from it. Built-ins register\n * themselves on import; third parties can register their own the same way.\n */\nconst adapters: SourceAdapter[] = []\nconst processors: Processor[] = []\n\nexport function registerAdapter(adapter: SourceAdapter): void {\n adapters.push(adapter)\n}\n\nexport function registerProcessor(processor: Processor): void {\n processors.push(processor)\n}\n\nexport function getAdapters(): SourceAdapter[] {\n return [...adapters]\n}\n\nexport function getProcessors(): Processor[] {\n return [...processors]\n}\n","/**\n * The normalized session model — the contract every processor reads and every\n * adapter produces. Adapters translate a vendor's transcript into this shape;\n * processors and the store never need to know which harness a session came from.\n *\n * `raw` is always preserved as an escape hatch for processors that need\n * vendor-specific detail the canonical view doesn't capture.\n */\n\n/** Vendor-neutral classification of a tool call. The per-vendor mapping lives in the adapter. */\nexport type CanonicalAction =\n | 'file_write'\n | 'file_read'\n | 'shell'\n | 'search'\n | 'task_spawn'\n | 'mcp_call'\n | 'web'\n | 'todo'\n | 'skill'\n | 'other'\n\nexport interface TokenUsage {\n input: number\n output: number\n cacheCreate: number\n cacheRead: number\n}\n\nexport function emptyUsage(): TokenUsage {\n return { input: 0, output: 0, cacheCreate: 0, cacheRead: 0 }\n}\n\nexport function addUsage(a: TokenUsage, b: TokenUsage): TokenUsage {\n return {\n input: a.input + b.input,\n output: a.output + b.output,\n cacheCreate: a.cacheCreate + b.cacheCreate,\n cacheRead: a.cacheRead + b.cacheRead,\n }\n}\n\nexport type ContentBlock =\n | { type: 'text'; text: string }\n | { type: 'thinking'; text: string }\n | { type: 'tool_use'; id: string; name: string; input: unknown }\n | { type: 'tool_result'; toolUseId: string; isError: boolean; content: unknown }\n\ninterface BaseEvent {\n uuid?: string\n parentUuid?: string | null\n ts?: string\n isSidechain: boolean\n /**\n * Dense ordinal over MAIN-THREAD events (sidechain events have none), assigned\n * post-merge by assignSeq() (core/blocks.ts). The coordinate the block partition\n * is defined in; persisted in the session blob.\n */\n seq?: number\n /**\n * For sidechain (subagent) events, the stable id of the subagent that emitted\n * them — Claude Code's per-subagent transcript id. Lets the viewer group a\n * subagent's turns into their own thread instead of interleaving them with the\n * main conversation. Undefined for main-thread events.\n */\n agentId?: string\n}\n\nexport interface UserMessage extends BaseEvent {\n kind: 'user'\n text: string\n blocks: ContentBlock[]\n}\n\nexport interface AssistantMessage extends BaseEvent {\n kind: 'assistant'\n model?: string\n blocks: ContentBlock[]\n usage: TokenUsage\n /**\n * Native cost (USD) for this message as reported by the source, when the source\n * computes its own cost (e.g. OpenCode, which routes to many providers tuneloop's\n * rate table doesn't cover). Used by computeSessionCost as a fallback when the\n * model has no entry in models.json. Absent for sources priced from tokens.\n */\n costUsd?: number\n}\n\nexport interface SystemEvent extends BaseEvent {\n kind: 'system'\n subtype?: string\n text?: string\n}\n\nexport type Event = UserMessage | AssistantMessage | SystemEvent\n\n/** A tool_use joined to its tool_result, classified into a canonical action. */\nexport interface ToolCall {\n id: string\n name: string\n action: CanonicalAction\n input: unknown\n /** Normalized fields per action (paths for file ops, command for shell, etc.). */\n target: { paths?: string[]; command?: string }\n result: { ok: boolean; isError: boolean; raw?: unknown }\n isSidechain: boolean\n ts?: string\n durationMs?: number\n}\n\n/**\n * A subagent (sidechain) spawned within a session. Claude Code writes each\n * subagent's transcript to its own file with a sibling `.meta.json`; this is the\n * normalized view of that metadata. `toolUseId` is the id of the spawning tool\n * call (the `Task`/`Agent` tool_use) in the parent thread, which lets the viewer\n * link that call to the subagent's transcript. Workflow subagents have no\n * spawning tool call, so `toolUseId` is absent for them.\n */\nexport interface SubagentMeta {\n agentId: string\n agentType?: string\n description?: string\n toolUseId?: string\n}\n\nexport interface Session {\n /** Namespaced id, e.g. `claude-code:<uuid>` — unique across vendors. */\n id: string\n /** Raw vendor session id. */\n sessionId: string\n /** Adapter / harness id, e.g. `claude-code`. */\n source: string\n /** LLM vendor family for slicing, e.g. `anthropic`. */\n provider: string\n title?: string\n /**\n * For a child transcript that lives in its own file (Codex sub-agent or `/fork`),\n * the parent session's raw id. Used to (a) fold sub-agents into the parent as\n * sidechains and (b) trim the replayed parent prefix both kinds inherit\n * (see analyze.ts / merge.ts, ADR-0005). Undefined for top-level sessions.\n */\n forkedFromId?: string\n /**\n * True only for a sub-agent (sidechain) child. Distinguishes it from a `/fork`,\n * which also carries `forkedFromId` but is its own top-level session: only\n * sub-agents fold into the parent group (ADR-0005).\n */\n isSubagent?: boolean\n project: { cwd?: string; repo?: string; branch?: string }\n startedAt?: string\n endedAt?: string\n /** Distinct models seen across assistant messages (model is per-message). */\n models: string[]\n /** Rolled-up token usage across all assistant messages (incl. sidechains). */\n tokens: TokenUsage\n events: Event[]\n /** Flattened convenience view of every tool call, incl. sidechains. */\n toolCalls: ToolCall[]\n /** Subagents spawned in this session (one per sidechain transcript). */\n subagents?: SubagentMeta[]\n raw: { path: string; contentHash: string }\n}\n","{\n \"_comment\": \"Per-million-token USD rates. cache_write_5m = 1.25x input (ephemeral 5-minute TTL, what Claude Code uses), cache_write_1h = 2x input, cache_read = 0.1x input. Update as vendor prices change; community PRs welcome. NOTE: cache_write_* are an Anthropic concept; OpenAI has no cache-write charge (Codex sets cacheCreate=0), so the openai entries set cache_write_* = input as inert placeholders and only cache_read is meaningful\",\n \"anthropic\": {\n \"claude-fable-5\": { \"input\": 10.0, \"output\": 50.0, \"cache_write_5m\": 12.5, \"cache_write_1h\": 20.0, \"cache_read\": 1.0 },\n \"claude-opus-4-8\": { \"input\": 5.0, \"output\": 25.0, \"cache_write_5m\": 6.25, \"cache_write_1h\": 10.0, \"cache_read\": 0.5 },\n \"claude-opus-4-7\": { \"input\": 5.0, \"output\": 25.0, \"cache_write_5m\": 6.25, \"cache_write_1h\": 10.0, \"cache_read\": 0.5 },\n \"claude-opus-4-6\": { \"input\": 5.0, \"output\": 25.0, \"cache_write_5m\": 6.25, \"cache_write_1h\": 10.0, \"cache_read\": 0.5 },\n \"claude-sonnet-4-6\": { \"input\": 3.0, \"output\": 15.0, \"cache_write_5m\": 3.75, \"cache_write_1h\": 6.0, \"cache_read\": 0.3 },\n \"claude-haiku-4-5\": { \"input\": 1.0, \"output\": 5.0, \"cache_write_5m\": 1.25, \"cache_write_1h\": 2.0, \"cache_read\": 0.1 }\n },\n \"openai\": {\n \"gpt-5.5\": { \"input\": 5.0, \"output\": 30.0, \"cache_write_5m\": 5.0, \"cache_write_1h\": 5.0, \"cache_read\": 0.5 },\n \"gpt-5.4\": { \"input\": 2.5, \"output\": 15.0, \"cache_write_5m\": 2.5, \"cache_write_1h\": 2.5, \"cache_read\": 0.25 },\n \"gpt-5.4-mini\": { \"input\": 0.75, \"output\": 4.5, \"cache_write_5m\": 0.75, \"cache_write_1h\": 0.75, \"cache_read\": 0.075 },\n \"gpt-5.2\": { \"input\": 2.5, \"output\": 15.0, \"cache_write_5m\": 2.5, \"cache_write_1h\": 2.5, \"cache_read\": 0.25 },\n \"gpt-5.3-codex\": { \"input\": 1.75, \"output\": 14.0, \"cache_write_5m\": 1.75, \"cache_write_1h\": 1.75, \"cache_read\": 0.175 },\n \"gpt-5.2-codex\": { \"input\": 1.75, \"output\": 14.0, \"cache_write_5m\": 1.75, \"cache_write_1h\": 1.75, \"cache_read\": 0.175 },\n \"gpt-5.1-codex-max\": { \"input\": 1.25, \"output\": 10.0, \"cache_write_5m\": 1.25, \"cache_write_1h\": 1.25, \"cache_read\": 0.125 },\n \"gpt-5.1-codex\": { \"input\": 1.25, \"output\": 10.0, \"cache_write_5m\": 1.25, \"cache_write_1h\": 1.25, \"cache_read\": 0.125 }\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport type { ModelPrice } from './pricing'\n\n/**\n * OpenRouter price backfill: a cache-first loader for OpenRouter's free, no-auth\n * model catalog (~400 models across every vendor), used to price an enrichment\n * model the static `models.json` doesn't list (loaded only then; see analyze.ts).\n * Best-effort and never blocking: a failed/offline fetch falls back to a stale\n * cache or an empty table → $0.\n *\n * `priceFor` (pricing.ts) consults this AFTER the static table, so the static\n * rates always win and this only fills genuine gaps.\n */\nconst ENDPOINT = 'https://openrouter.ai/api/v1/models'\nconst CACHE_FILE = 'openrouter-prices.json'\nconst CACHE_TTL_MS = 24 * 60 * 60 * 1000 // a day; prices drift slowly\n\ninterface RawModel {\n id: string\n pricing?: { prompt?: string; completion?: string; input_cache_read?: string; input_cache_write?: string }\n}\ninterface Cache {\n fetchedAt: number\n prices: Record<string, ModelPrice>\n}\n\n// Loaded once per process; null until loadOpenRouterPrices runs (synchronous\n// lookups before then simply miss). Keyed by OpenRouter model id (e.g.\n// \"deepseek/deepseek-chat\"), normalized to our per-million ModelPrice.\nlet table: Map<string, ModelPrice> | null = null\n\n/** Load the catalog (cache-first). Safe to call once at startup; idempotent. */\nexport async function loadOpenRouterPrices(dataDir: string, log?: { debug(msg: string): void }): Promise<void> {\n if (table) return\n const path = join(dataDir, CACHE_FILE)\n const cached = await readCache(path)\n if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {\n table = new Map(Object.entries(cached.prices))\n return\n }\n try {\n const resp = await fetch(ENDPOINT, { signal: AbortSignal.timeout(4000) })\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`)\n const json = (await resp.json()) as { data?: RawModel[] }\n const prices = normalize(json.data ?? [])\n table = new Map(Object.entries(prices))\n await writeCache(path, { fetchedAt: Date.now(), prices })\n } catch (err) {\n table = cached ? new Map(Object.entries(cached.prices)) : new Map()\n log?.debug(`openrouter price fetch failed (${(err as Error).message}); using ${cached ? 'stale cache' : 'no'} backfill`)\n }\n}\n\n/**\n * Synchronous backfill lookup (post-load). Tries the more-specific\n * `\"<provider>/<model>\"` first (e.g. deepseek + deepseek-chat), then the bare id\n * (already a full OpenRouter id when provider=openrouter). Miss → undefined → $0.\n */\nexport function backfillPrice(provider: string, model: string): ModelPrice | undefined {\n if (!table || !model) return undefined\n return table.get(`${provider}/${model}`) ?? table.get(model)\n}\n\nfunction normalize(models: RawModel[]): Record<string, ModelPrice> {\n const out: Record<string, ModelPrice> = {}\n for (const m of models) {\n const p = m.pricing\n if (!m.id || !p) continue\n const perM = (v: string | undefined) => (v ? Number(v) * 1_000_000 : 0)\n const input = perM(p.prompt)\n // OpenRouter has a single cache-write rate; mirror it to both TTL slots.\n const cacheWrite = p.input_cache_write ? perM(p.input_cache_write) : input\n out[m.id] = {\n input,\n output: perM(p.completion),\n cache_write_5m: cacheWrite,\n cache_write_1h: cacheWrite,\n cache_read: perM(p.input_cache_read),\n }\n }\n return out\n}\n\nasync function readCache(path: string): Promise<Cache | null> {\n try {\n const c = JSON.parse(await readFile(path, 'utf8')) as Cache\n // Validate shape so a truncated/half-written cache is treated as a miss, not\n // a crash (Object.entries(undefined) downstream).\n if (typeof c?.fetchedAt !== 'number' || !c.prices || typeof c.prices !== 'object') return null\n return c\n } catch {\n return null\n }\n}\n\nasync function writeCache(path: string, cache: Cache): Promise<void> {\n try {\n await mkdir(dirname(path), { recursive: true })\n await writeFile(path, JSON.stringify(cache))\n } catch {\n // A non-writable data dir just means no cache next run; not fatal.\n }\n}\n","import type { Session, TokenUsage } from '../core/model'\nimport models from './models.json'\nimport { backfillPrice } from './openrouter'\n\nexport interface ModelPrice {\n input: number\n output: number\n cache_write_5m: number\n cache_write_1h: number\n cache_read: number\n}\n\n/** Bump when models.json rates change so stored costs can be recomputed. */\nexport const PRICE_TABLE_VERSION = '2026-06-30'\n\ntype Table = Record<string, Record<string, ModelPrice>>\nconst TABLE = models as unknown as Table\n\n/**\n * Look up a price, tolerant of model-id drift: exact match, then strip a\n * trailing date snapshot (`-20251001` or `@20251001`), then prefix match.\n */\nexport function priceFor(provider: string, model: string, opts?: { backfill?: boolean }): ModelPrice | undefined {\n const byProvider = TABLE[provider]\n if (byProvider) {\n if (byProvider[model]) return byProvider[model]\n const stripped = model.replace(/[-@]\\d{8}$/, '')\n if (byProvider[stripped]) return byProvider[stripped]\n // Longest key first so a specific variant wins over a shorter prefix of it\n // (e.g. `gpt-5.2-codex-*` matches `gpt-5.2-codex`, not the pricier `gpt-5.2`).\n for (const key of Object.keys(byProvider).sort((a, b) => b.length - a.length)) {\n if (model.startsWith(key)) return byProvider[key]\n }\n }\n // Backfill is opt-in (enrichment self-cost only, never session analytics)\n return opts?.backfill ? backfillPrice(provider, model) : undefined\n}\n\n/** Cost of a single usage record at a given model's rates (0 if unpriced). */\nexport function costOfUsage(provider: string, model: string, u: TokenUsage): number {\n // Enrichment self-cost: opt into the backfill to price non-table providers\n const p = priceFor(provider, model, { backfill: true })\n if (!p) return 0\n return (\n (u.input * p.input + u.output * p.output + u.cacheCreate * p.cache_write_5m + u.cacheRead * p.cache_read) /\n 1_000_000\n )\n}\n\n/**\n * Usage + cost for one assistant message — the atomic grain of token economics.\n * Persisted to `usage_facts` so model / main-vs-sidechain / time breakdowns are\n * all read-time GROUP BYs (cost can't be summed by model off the session row).\n */\nexport interface UsageFact {\n idx: number\n model: string\n isSidechain: boolean\n ts?: string\n tokens: TokenUsage\n usd: number\n}\n\nexport interface CostResult {\n usd: number\n /** Models we had no price for — their tokens count, but contribute $0. */\n unpriced: string[]\n /** One entry per assistant message, in order. Sums to `usd` / session tokens. */\n facts: UsageFact[]\n}\n\n/**\n * Cost of a session, priced per assistant message at that message's model, with\n * the per-message breakdown retained. Cache-creation tokens are priced at the\n * 5-minute rate (Claude Code's default).\n */\nexport function computeSessionCost(session: Session): CostResult {\n let usd = 0\n const unpriced = new Set<string>()\n const facts: UsageFact[] = []\n let idx = 0\n for (const ev of session.events) {\n if (ev.kind !== 'assistant') continue\n const model = ev.model ?? session.models[0] ?? '<unknown>'\n const u = ev.usage\n let cost = 0\n if (ev.costUsd != null) {\n // The source billed its own cost (e.g. OpenCode) — the actual spend, which is\n // authoritative over our rate-table reconstruction even if the table has the model.\n cost = ev.costUsd\n } else {\n // Token-priced sources (Claude Code, Codex); static table only, no backfill.\n const price = priceFor(session.provider, model)\n if (price) {\n cost =\n (u.input * price.input +\n u.output * price.output +\n u.cacheCreate * price.cache_write_5m +\n u.cacheRead * price.cache_read) /\n 1_000_000\n } else {\n unpriced.add(model)\n }\n }\n usd += cost\n facts.push({ idx, model, isSidechain: ev.isSidechain, ts: ev.ts, tokens: u, usd: cost })\n idx++\n }\n return { usd, unpriced: [...unpriced], facts }\n}\n","import { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { registerAdapter } from '../../core/registry'\nimport { walkFiles } from '../../util/walk'\nimport type { SourceAdapter } from '../types'\nimport { parseClaudeCode, PARSE_VERSION } from './parse'\n\nexport const claudeCodeAdapter: SourceAdapter = {\n id: 'claude-code',\n provider: 'anthropic',\n parseVersion: PARSE_VERSION,\n defaultRoots: () => [join(homedir(), '.claude', 'projects')],\n discover: async (roots) => {\n const all: string[] = []\n for (const root of roots) all.push(...(await walkFiles(root, '.jsonl')))\n return all\n },\n parse: parseClaudeCode,\n}\n\nregisterAdapter(claudeCodeAdapter)\n","import { readdir } from 'node:fs/promises'\nimport { join } from 'node:path'\n\n/** Recursively collect files under `root` ending in `ext`. Missing dirs yield []. */\nexport async function walkFiles(root: string, ext: string): Promise<string[]> {\n const out: string[] = []\n const rec = async (dir: string): Promise<void> => {\n let entries\n try {\n entries = await readdir(dir, { withFileTypes: true })\n } catch {\n return\n }\n for (const e of entries) {\n const full = join(dir, e.name)\n if (e.isDirectory()) await rec(full)\n else if (e.isFile() && e.name.endsWith(ext)) out.push(full)\n }\n }\n await rec(root)\n return out\n}\n","import { readFile } from 'node:fs/promises'\nimport { basename } from 'node:path'\nimport { contentHash } from '../../core/hash'\nimport { addUsage, emptyUsage } from '../../core/model'\nimport type {\n AssistantMessage,\n ContentBlock,\n Event,\n Session,\n SubagentMeta,\n SystemEvent,\n TokenUsage,\n ToolCall,\n UserMessage,\n} from '../../core/model'\nimport { mapAction } from './actions'\n\n// Bump when ingest-time derivation changes so stored sessions are rebuilt on the\n// same bytes (see analyze.ts). 4: capture subagent identity (agentId on events +\n// session.subagents from the sidechain `.meta.json`) for the tabbed transcript.\n// 5: assign main-thread `seq` (assignSeq) so the block partition + blob carry it.\n// 6: skip <synthetic> messages — emit api errors as SystemEvent, drop no-ops.\nexport const PARSE_VERSION = 6\nconst SOURCE = 'claude-code'\nconst PROVIDER = 'anthropic'\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ntype Raw = any\n\n/**\n * Parse a Claude Code `.jsonl` transcript into the normalized model. Handles the\n * line-type zoo (keeps user/assistant/system, ignores mode/snapshot/attachment),\n * joins tool_use blocks to their results (incl. the top-level `toolUseResult`),\n * and rolls up per-message token usage including sidechain (Task) turns.\n */\nexport async function parseClaudeCode(path: string): Promise<Session | null> {\n const content = await readFile(path, 'utf8')\n const records: Raw[] = []\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed) continue\n try {\n records.push(JSON.parse(trimmed))\n } catch {\n /* skip malformed line */\n }\n }\n const hasMessages = records.some((r) => (r.type === 'assistant' || r.type === 'user') && r.message)\n if (!hasMessages) return null\n\n const sessionId = firstString(records, 'sessionId') ?? basename(path).replace(/\\.jsonl$/, '')\n\n // tool_use_id -> result, gathered from user-turn tool_result blocks + top-level toolUseResult.\n const resultById = new Map<string, { content: unknown; isError: boolean; toolUseResult?: unknown }>()\n for (const r of records) {\n if (r.type === 'user' && r.message && Array.isArray(r.message.content)) {\n for (const b of r.message.content) {\n if (b && b.type === 'tool_result' && typeof b.tool_use_id === 'string') {\n resultById.set(b.tool_use_id, {\n content: b.content,\n isError: !!b.is_error,\n toolUseResult: r.toolUseResult,\n })\n }\n }\n }\n }\n\n const events: Event[] = []\n const toolCalls: ToolCall[] = []\n // Distinct subagent ids seen in this file. Claude Code writes each subagent to\n // its own transcript file (records tagged with `agentId`), so in practice this\n // is a single id, but we collect the set to stay robust.\n const agentIds = new Set<string>()\n const models = new Set<string>()\n let tokens = emptyUsage()\n let title: string | undefined\n let cwd: string | undefined\n let branch: string | undefined\n let firstTs: string | undefined\n let lastTs: string | undefined\n\n for (const r of records) {\n const ts: string | undefined = typeof r.timestamp === 'string' ? r.timestamp : undefined\n if (ts) {\n if (!firstTs) firstTs = ts\n lastTs = ts\n }\n if (typeof r.cwd === 'string') cwd = r.cwd\n if (typeof r.gitBranch === 'string') branch = r.gitBranch\n if (r.type === 'ai-title' && typeof r.aiTitle === 'string') title = r.aiTitle\n\n const isSidechain = !!r.isSidechain\n const agentId = typeof r.agentId === 'string' ? r.agentId : undefined\n if (agentId) agentIds.add(agentId)\n\n if (r.type === 'assistant' && r.message) {\n const m = r.message\n\n // Claude Code injects synthetic assistant messages (model: \"<synthetic>\")\n // for API errors and no-op turns — these never hit the real model.\n if (m.model === '<synthetic>') {\n // add only only if it's a terminal api error, skip no-op\n if (r.isApiErrorMessage) {\n const text = Array.isArray(m.content)\n ? m.content.filter((b: Raw) => b?.type === 'text').map((b: Raw) => String(b.text ?? '')).join('\\n')\n : undefined\n const ev: SystemEvent = {\n kind: 'system',\n uuid: r.uuid,\n parentUuid: r.parentUuid ?? null,\n ts,\n isSidechain,\n agentId,\n subtype: 'api_error',\n text,\n }\n events.push(ev)\n }\n continue // skip adding to token accumulator and models\n }\n\n const model: string | undefined = typeof m.model === 'string' ? m.model : undefined\n if (model) models.add(model)\n const usage = usageOf(m.usage)\n tokens = addUsage(tokens, usage)\n\n const blocks: ContentBlock[] = []\n if (Array.isArray(m.content)) {\n for (const b of m.content) {\n if (!b || typeof b !== 'object') continue\n if (b.type === 'text') blocks.push({ type: 'text', text: String(b.text ?? '') })\n else if (b.type === 'thinking') blocks.push({ type: 'thinking', text: String(b.thinking ?? '') })\n else if (b.type === 'tool_use' && typeof b.id === 'string') {\n const name = String(b.name ?? '')\n blocks.push({ type: 'tool_use', id: b.id, name, input: b.input })\n const res = resultById.get(b.id)\n const mapped = mapAction(name, b.input)\n toolCalls.push({\n id: b.id,\n // Analytics identity: refined for skills (specific skill name),\n // raw tool name otherwise. The transcript block above keeps the literal name.\n name: mapped.name ?? name,\n action: mapped.action,\n input: b.input,\n target: mapped.target,\n result: { ok: !res?.isError, isError: !!res?.isError, raw: res?.toolUseResult ?? res?.content },\n isSidechain,\n ts,\n })\n }\n }\n }\n const ev: AssistantMessage = {\n kind: 'assistant',\n uuid: r.uuid,\n parentUuid: r.parentUuid ?? null,\n ts,\n isSidechain,\n agentId,\n model,\n blocks,\n usage,\n }\n events.push(ev)\n } else if (r.type === 'user' && r.message) {\n const content = r.message.content\n const blocks: ContentBlock[] = []\n let text = ''\n if (typeof content === 'string') {\n text = content\n } else if (Array.isArray(content)) {\n for (const b of content) {\n if (!b || typeof b !== 'object') continue\n if (b.type === 'text') {\n text += (text ? '\\n' : '') + String(b.text ?? '')\n blocks.push({ type: 'text', text: String(b.text ?? '') })\n } else if (b.type === 'tool_result' && typeof b.tool_use_id === 'string') {\n blocks.push({ type: 'tool_result', toolUseId: b.tool_use_id, isError: !!b.is_error, content: b.content })\n }\n }\n }\n const ev: UserMessage = {\n kind: 'user',\n uuid: r.uuid,\n parentUuid: r.parentUuid ?? null,\n ts,\n isSidechain,\n agentId,\n text,\n blocks,\n }\n events.push(ev)\n } else if (r.type === 'system') {\n const ev: SystemEvent = {\n kind: 'system',\n uuid: r.uuid,\n parentUuid: r.parentUuid ?? null,\n ts,\n isSidechain,\n agentId,\n subtype: typeof r.subtype === 'string' ? r.subtype : undefined,\n text: typeof r.content === 'string' ? r.content : undefined,\n }\n events.push(ev)\n }\n }\n\n // Skip sessions where the model was never reached (e.g. only synthetic messages).\n if (!events.some((e) => e.kind === 'assistant')) return null\n\n // A sidechain file carries one (occasionally more) subagent. Its sibling\n // `<file>.meta.json` names the subagent type/description and the parent tool\n // call that spawned it, which is what links the call to this transcript.\n let subagents: SubagentMeta[] | undefined\n if (agentIds.size) {\n const meta = await readAgentMeta(path)\n subagents = [...agentIds].map((agentId) => ({ agentId, ...meta }))\n }\n\n return {\n id: `${SOURCE}:${sessionId}`,\n sessionId,\n source: SOURCE,\n provider: PROVIDER,\n title,\n project: { cwd, branch },\n startedAt: firstTs,\n endedAt: lastTs,\n models: [...models],\n tokens,\n events,\n toolCalls,\n subagents,\n raw: { path, contentHash: contentHash(content) },\n }\n}\n\n/**\n * Read a subagent transcript's sibling `<file>.meta.json`, returning the fields\n * the viewer links/labels by. Missing or malformed → empty (the subagent still\n * gets a tab keyed by its `agentId`, just without a type/description/link).\n */\nasync function readAgentMeta(jsonlPath: string): Promise<Omit<SubagentMeta, 'agentId'>> {\n const metaPath = jsonlPath.replace(/\\.jsonl$/, '.meta.json')\n if (metaPath === jsonlPath) return {}\n try {\n const m = JSON.parse(await readFile(metaPath, 'utf8')) as Raw\n return {\n agentType: typeof m.agentType === 'string' ? m.agentType : undefined,\n description: typeof m.description === 'string' ? m.description : undefined,\n toolUseId: typeof m.toolUseId === 'string' ? m.toolUseId : undefined,\n }\n } catch {\n return {}\n }\n}\n\nfunction usageOf(u: Raw): TokenUsage {\n if (!u || typeof u !== 'object') return emptyUsage()\n return {\n input: num(u.input_tokens),\n output: num(u.output_tokens),\n cacheCreate: num(u.cache_creation_input_tokens),\n cacheRead: num(u.cache_read_input_tokens),\n }\n}\n\nfunction num(v: unknown): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : 0\n}\n\nfunction firstString(records: Raw[], key: string): string | undefined {\n for (const r of records) if (typeof r[key] === 'string') return r[key]\n return undefined\n}\n","import { createHash } from 'node:crypto'\n\n/** Content hash of a session's raw transcript — the cache key input. */\nexport function contentHash(content: string): string {\n return createHash('sha256').update(content).digest('hex').slice(0, 32)\n}\n","import type { CanonicalAction } from '../../core/model'\n\n/**\n * Map Claude Code tool names to canonical actions. This is the ONE place that\n * knows Claude Code's tool vocabulary — common extractors stay vendor-neutral.\n */\nconst FILE_WRITE = new Set(['Write', 'Edit', 'MultiEdit', 'NotebookEdit'])\nconst FILE_READ = new Set(['Read', 'NotebookRead'])\nconst SEARCH = new Set(['Glob', 'Grep'])\nconst WEB = new Set(['WebFetch', 'WebSearch'])\nconst SHELL = new Set(['Bash', 'BashOutput', 'KillShell', 'KillBash'])\n\nexport interface MappedAction {\n action: CanonicalAction\n target: { paths?: string[]; command?: string }\n /**\n * Optional refined identity for the tool-call `name`. Used for skills, whose\n * raw tool name is the generic `Skill` — we surface the specific skill (e.g.\n * `frontend-design:frontend-design`) so it's groupable/filterable, mirroring\n * how MCP keeps the specific tool in `name` under `action='mcp_call'`.\n */\n name?: string\n}\n\nexport function mapAction(name: string, input: unknown): MappedAction {\n const obj = (input && typeof input === 'object' ? input : {}) as Record<string, unknown>\n if (name.startsWith('mcp__')) return { action: 'mcp_call', target: {} }\n if (FILE_WRITE.has(name)) return { action: 'file_write', target: { paths: filePaths(obj) } }\n if (FILE_READ.has(name)) return { action: 'file_read', target: { paths: filePaths(obj) } }\n if (SHELL.has(name)) {\n return { action: 'shell', target: { command: typeof obj.command === 'string' ? obj.command : undefined } }\n }\n if (SEARCH.has(name)) return { action: 'search', target: {} }\n if (name === 'Task' || name === 'Agent') return { action: 'task_spawn', target: {} }\n if (name === 'TodoWrite') return { action: 'todo', target: {} }\n if (name === 'Skill') {\n const skill = typeof obj.skill === 'string' ? obj.skill : undefined\n return { action: 'skill', target: {}, name: skill }\n }\n if (WEB.has(name)) return { action: 'web', target: {} }\n return { action: 'other', target: {} }\n}\n\nfunction filePaths(obj: Record<string, unknown>): string[] | undefined {\n const p = obj.file_path ?? obj.notebook_path ?? obj.path\n return typeof p === 'string' ? [p] : undefined\n}\n","import { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { registerAdapter } from '../../core/registry'\nimport { walkFiles } from '../../util/walk'\nimport type { SourceAdapter } from '../types'\nimport { parseCodex, PARSE_VERSION } from './parse'\n\nexport const codexAdapter: SourceAdapter = {\n id: 'codex',\n provider: 'openai',\n parseVersion: PARSE_VERSION,\n defaultRoots: () => [join(homedir(), '.codex', 'sessions')],\n discover: async (roots) => {\n const all: string[] = []\n for (const root of roots) all.push(...(await walkFiles(root, '.jsonl')))\n return all\n },\n parse: parseCodex,\n}\n\nregisterAdapter(codexAdapter)\n","import { readFile } from 'node:fs/promises'\nimport { basename } from 'node:path'\nimport { contentHash } from '../../core/hash'\nimport { addUsage, emptyUsage } from '../../core/model'\nimport type {\n AssistantMessage,\n ContentBlock,\n Event,\n Session,\n SubagentMeta,\n SystemEvent,\n TokenUsage,\n ToolCall,\n UserMessage,\n} from '../../core/model'\nimport { mapAction } from './actions'\n\n// Bump when ingest-time derivation changes so stored sessions are rebuilt on the\n// same bytes (composed with NORMALIZE_VERSION in analyze.ts). 1: initial Codex adapter.\nexport const PARSE_VERSION = 2\nconst SOURCE = 'codex'\nconst PROVIDER = 'openai'\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ntype Raw = any\n\n/**\n * Parse a Codex rollout `.jsonl` (the `rollout-*.jsonl` files under `~/.codex/sessions`)\n * into the normalized model. Lines are `{type, payload}`: `session_meta` (identity/cwd/git),\n * `turn_context` (the model), `response_item` (the API conversation), `event_msg`\n * (UI events incl. `token_count`). Usage lives only on `token_count` events, so one\n * assistant message is emitted per token_count, folding the response items since the\n * previous one. Sub-agent files (`thread_source: subagent`) are tagged as\n * sidechains for the parent merge\n */\nexport async function parseCodex(path: string): Promise<Session | null> {\n const content = await readFile(path, 'utf8')\n const records: Raw[] = []\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed) continue\n try {\n records.push(JSON.parse(trimmed))\n } catch {\n /* skip malformed line */\n }\n }\n\n const meta = records.find((r) => r.type === 'session_meta')?.payload as Raw\n if (!meta || typeof meta.id !== 'string') return null // not a Codex rollout\n\n const sessionId: string = meta.id\n const isSubagent = meta.thread_source === 'subagent'\n const forkedFromId: string | undefined =\n typeof meta.forked_from_id === 'string'\n ? meta.forked_from_id\n : typeof meta?.source?.subagent?.thread_spawn?.parent_thread_id === 'string'\n ? meta.source.subagent.thread_spawn.parent_thread_id\n : undefined\n\n // Two things gathered up front (some trail what they describe in file order):\n // - tool call_id -> output string, so a call joins its result regardless of order.\n // - the set of GENUINE human prompts. Codex echoes every real prompt as an\n // `event_msg.user_message`, but NOT its injected `role:user` machinery\n // (<environment_context>, <turn_aborted>, <subagent_notification>, …). So this\n // set is the oracle for telling a real user turn from machinery (ADR-0001 Q1),\n // which keeps core/turns.ts Claude-only and the block partition honest.\n const resultById = new Map<string, string>()\n const humanPrompts = new Set<string>()\n for (const r of records) {\n const p = r.payload as Raw\n if (!p) continue\n if (p.type === 'function_call_output' || p.type === 'custom_tool_call_output' || p.type === 'tool_search_output') {\n if (typeof p.call_id === 'string') resultById.set(p.call_id, outputString(p.output ?? p.tools))\n } else if (r.type === 'event_msg' && p.type === 'user_message' && typeof p.message === 'string') {\n humanPrompts.add(p.message.trim())\n }\n }\n\n const events: Event[] = []\n const toolCalls: ToolCall[] = []\n const models = new Set<string>()\n let tokens = emptyUsage()\n let currentModel: string | undefined\n let firstTs: string | undefined\n let lastTs: string | undefined\n\n // Strategy 1 (ADR-0001): accumulate assistant-side blocks; each token_count flushes\n // them into one AssistantMessage carrying that inference call's usage.\n let pending: ContentBlock[] = []\n // Cumulative-total signature of the last counted token_count. Older-format rollouts\n // re-emit a token_count with an unchanged `total_token_usage` (a turn-finalize echo);\n // skipping those avoids double-counting (ADR-0005). Equal cumulative total ⟺ a re-emit,\n // since a real inference call always advances the total — so usage-only records survive.\n let prevTotalSig: string | null = null\n const flush = (usage: TokenUsage, ts: string | undefined): void => {\n if (!pending.length && usage === ZERO) return\n const ev: AssistantMessage = {\n kind: 'assistant',\n ts,\n isSidechain: isSubagent,\n agentId: isSubagent ? sessionId : undefined,\n model: currentModel,\n blocks: pending,\n usage,\n }\n events.push(ev)\n tokens = addUsage(tokens, usage)\n pending = []\n }\n\n for (const r of records) {\n const ts: string | undefined = typeof r.timestamp === 'string' ? r.timestamp : undefined\n if (ts) {\n if (!firstTs) firstTs = ts\n lastTs = ts\n }\n const p = r.payload as Raw\n if (!p) continue\n\n if (r.type === 'turn_context' && typeof p.model === 'string') {\n currentModel = p.model\n models.add(p.model)\n continue\n }\n\n if (r.type === 'event_msg' && p.type === 'token_count') {\n const info = p.info\n if (info && info.last_token_usage) {\n const total = info.total_token_usage\n const sig =\n total && (total.input_tokens != null || total.output_tokens != null)\n ? `${num(total.input_tokens)}/${num(total.output_tokens)}`\n : null\n // Skip a re-emit (same cumulative total as the previous count); a null sig\n // (no total) can't be deduped, so always count it.\n if (sig === null || sig !== prevTotalSig) {\n prevTotalSig = sig\n flush(mapUsage(info.last_token_usage), ts)\n }\n }\n continue\n }\n\n if (r.type !== 'response_item') continue\n\n if (p.type === 'message') {\n const text = textOf(p.content)\n if (p.role === 'assistant') {\n if (text) pending.push({ type: 'text', text })\n } else {\n // user / developer: a UserMessage only for a genuine human prompt (one Codex\n // echoed to event_msg.user_message). Everything else — developer role and\n // injected user-role machinery — becomes a SystemEvent.\n if (pending.length) flush(ZERO, ts) // flush any unaccounted assistant content first\n if (p.role === 'user' && humanPrompts.has(text.trim())) {\n const ev: UserMessage = {\n kind: 'user',\n ts,\n isSidechain: isSubagent,\n agentId: isSubagent ? sessionId : undefined,\n text,\n blocks: text ? [{ type: 'text', text }] : [],\n }\n events.push(ev)\n } else {\n const ev: SystemEvent = {\n kind: 'system',\n ts,\n isSidechain: isSubagent,\n agentId: isSubagent ? sessionId : undefined,\n subtype: p.role === 'developer' ? 'developer' : 'context',\n text,\n }\n events.push(ev)\n }\n }\n } else if (p.type === 'reasoning') {\n const summary = summaryText(p.summary)\n if (summary) pending.push({ type: 'thinking', text: summary })\n } else if (p.type === 'function_call' || p.type === 'custom_tool_call' || p.type === 'tool_search_call') {\n const name = String(p.name ?? (p.type === 'tool_search_call' ? 'tool_search' : ''))\n const callId = typeof p.call_id === 'string' ? p.call_id : `${name}-${toolCalls.length}`\n // function_call: `arguments` is a JSON STRING. custom_tool_call (apply_patch): `input`\n // is the raw patch text. tool_search_call: `arguments` is an object.\n const input = p.type === 'function_call' ? parseArgs(p.arguments) : (p.input ?? p.arguments)\n // `namespace` (function_call only) tags MCP tools as `mcp__<server>`.\n const namespace = typeof p.namespace === 'string' ? p.namespace : undefined\n const mapped = mapAction(name, input, namespace)\n const out = resultById.get(callId)\n // Derive failure from the tool's exit code across Codex's output shapes (shell\n // wrappers, apply_patch, and the structured `{metadata:{exit_code}}` form). MCP\n // calls carry no exit code, so fall back to Codex's `tool call error:` marker.\n // No signal at all → assume ok.\n const code = exitCodeOf(out)\n const isError = code != null ? code !== 0 : toolCallFailed(out)\n pending.push({ type: 'tool_use', id: callId, name, input }) // transcript block keeps the literal tool name\n toolCalls.push({\n id: callId,\n // Analytics identity: refined for skills (the specific skill name), raw tool name otherwise.\n name: mapped.name ?? name,\n action: mapped.action,\n input,\n target: mapped.target,\n result: { ok: !isError, isError, raw: out },\n isSidechain: isSubagent,\n ts,\n })\n }\n }\n\n // Trailing assistant content with no closing token_count → a zero-usage record.\n if (pending.length) flush(ZERO, lastTs)\n\n // A sub-agent file is one sidechain; its `agent_nickname` labels it. `toolUseId`\n // (the parent's spawn_agent call) is resolved during the parent merge (Phase 2).\n const subagents: SubagentMeta[] | undefined = isSubagent\n ? [{ agentId: sessionId, agentType: typeof meta.agent_nickname === 'string' ? meta.agent_nickname : undefined }]\n : undefined\n\n return {\n id: `${SOURCE}:${sessionId}`,\n sessionId,\n source: SOURCE,\n provider: PROVIDER,\n // TODO: Codex has no native session title — enrich from the transcript via LLM later (docs/TODO.md).\n title: undefined,\n project: { cwd: typeof meta.cwd === 'string' ? meta.cwd : undefined, branch: meta?.git?.branch },\n forkedFromId,\n isSubagent: isSubagent || undefined,\n startedAt: firstTs,\n endedAt: lastTs,\n models: [...models],\n tokens,\n events,\n toolCalls,\n subagents,\n raw: { path, contentHash: contentHash(content) },\n }\n}\n\n/**\n * Exit code for a tool call across Codex's output shapes, or null when none is present\n * (→ treated as success). Handles the structured `{output, metadata:{exit_code}}` form\n * and the line-anchored shell wrappers (`Process exited with code N` / `Exit code: N`),\n * matched before stdout so an echoed phrase can't masquerade as the status line.\n */\nfunction exitCodeOf(out: string | undefined): number | null {\n if (out == null) return null\n try {\n const o = JSON.parse(out)\n if (o && typeof o === 'object' && o.metadata && typeof o.metadata.exit_code === 'number') {\n return o.metadata.exit_code\n }\n } catch {\n /* not JSON — fall through to the text wrappers */\n }\n const m = /^(?:Process exited with code|Exit code:)\\s*(\\d+)/m.exec(out)\n return m ? parseInt(m[1]!, 10) : null\n}\n\n/**\n * A failed tool call with no exit code — three Codex framings, none carrying one:\n * - MCP/tool transport failures get a literal `tool call error:` prefix.\n * - A command that never ran (binary missing, or a sandbox/spawn error) is framed\n * `exec_command failed for ...` — exec-tool-specific, line-anchored so echoed\n * stdout can't masquerade as the status line.\n * - A user-DENIED approval. Codex attaches the decision reason `rejected by user`\n * regardless of which tool was gated, so we match THAT rather than any one\n * tool's wrapper — an exec rejection arrives as `exec_command failed for ...:\n * CreateProcess { message: \"Rejected(\"rejected by user\")\" }`, but a denied\n * apply_patch / MCP call won't carry the exec prefix. Without this a rejected\n * call reads as a success (no exit code, no `tool call error:`) and vanishes.\n * Keep in sync with error-category's `user_rejected` rule (same text).\n */\nfunction toolCallFailed(out: string | undefined): boolean {\n if (out == null) return false\n return out.includes('tool call error:') || /^exec_command failed for /m.test(out) || out.includes('rejected by user')\n}\n\n/** Shared zero-usage sentinel — its identity flags a content-only (no token_count) flush. */\nconst ZERO: TokenUsage = emptyUsage()\n\nfunction mapUsage(last: Raw): TokenUsage {\n const cached = num(last.cached_input_tokens)\n return {\n input: Math.max(0, num(last.input_tokens) - cached), // input_tokens INCLUDES cached\n output: num(last.output_tokens), // INCLUDES reasoning_output_tokens\n cacheCreate: 0, // OpenAI has no cache-write charge\n cacheRead: cached,\n }\n}\n\n/** Join a Codex message's content array into plain text (`input_text` / `output_text`). */\nfunction textOf(content: unknown): string {\n if (typeof content === 'string') return content\n if (!Array.isArray(content)) return ''\n const parts: string[] = []\n for (const b of content) {\n if (b && typeof b === 'object' && typeof (b as Raw).text === 'string') parts.push((b as Raw).text)\n }\n return parts.join('\\n')\n}\n\n/** Codex reasoning summary: array of `{ type: 'summary_text', text }`. */\nfunction summaryText(summary: unknown): string {\n if (!Array.isArray(summary)) return ''\n return summary\n .map((s) => (s && typeof s === 'object' && typeof (s as Raw).text === 'string' ? (s as Raw).text : ''))\n .filter(Boolean)\n .join('\\n')\n}\n\nfunction outputString(output: unknown): string {\n return typeof output === 'string' ? output : JSON.stringify(output ?? '')\n}\n\nfunction parseArgs(args: unknown): unknown {\n if (typeof args !== 'string') return args ?? {}\n try {\n return JSON.parse(args)\n } catch {\n return { _raw: args }\n }\n}\n\nfunction num(v: unknown): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : 0\n}\n","import type { CanonicalAction } from '../../core/model'\n\n/**\n * Map Codex tool names to canonical actions. This is the ONE place that knows\n * Codex's tool vocabulary — common extractors stay vendor-neutral.\n *\n * Codex reads files via the shell (no `Read` tool). MCP tools arrive as namespaced\n * `function_call`s (`mcp__<server>`) and map to `action='mcp_call'`. Skills are\n * shell-based — `SKILL.md` + scripts — but a skill is loaded by reading its\n * `SKILL.md`, which we recognize and reclassify to `action='skill'` so Codex skills\n * join Claude's in one facet. Anything unmapped falls through to `other`.\n */\nexport interface MappedAction {\n action: CanonicalAction\n target: { paths?: string[]; command?: string }\n /** Refined tool-call identity: the skill name for `skill`, the `mcp__server__tool` id for `mcp_call`. */\n name?: string\n}\n\n// A skill is engaged by reading `<agent-home>/skills/[.system/]<name>/SKILL.md`. The\n// home is NOT fixed — `.codex` (default), `.agents` (shared), or a relocated CODEX_HOME\n// — so anchor on a dot-prefixed home dir: matches any `.X/skills/`, excludes ordinary\n// project `skills/` dirs. A non-dot relocated home is the documented residual.\nconst SKILL_RE = /\\/\\.[\\w-]+\\/skills\\/(?:\\.system\\/)?([\\w-]+)\\/SKILL\\.md\\b/\n\nexport function mapAction(name: string, input: unknown, namespace?: string): MappedAction {\n // MCP tools carry a `mcp__<server>` namespace with the bare tool in `name` (built-ins\n // also have namespaces, e.g. `multi_agent_v1`, so match the prefix, not mere presence).\n // Rebuild Claude's `mcp__<server>__<tool>` identity so both harnesses share one facet.\n if (namespace?.startsWith('mcp__')) {\n return { action: 'mcp_call', name: `${namespace}__${name}`, target: {} }\n }\n switch (name) {\n case 'exec_command':\n case 'shell_command': {\n const obj = (input && typeof input === 'object' ? input : {}) as Record<string, unknown>\n const command = typeof obj.cmd === 'string' ? obj.cmd : typeof obj.command === 'string' ? obj.command : undefined\n // The SKILL.md read = a skill being engaged; its later script runs stay 'shell'.\n // May occasionally catch a skill inspected/edited rather than used \n const skill = command ? SKILL_RE.exec(command)?.[1] : undefined\n if (skill) return { action: 'skill', name: skill, target: { command } }\n return { action: 'shell', target: { command } }\n }\n case 'apply_patch':\n // `input` is the raw patch text (\"*** Begin Patch ... *** End Patch\").\n return { action: 'file_write', target: { paths: patchPaths(typeof input === 'string' ? input : '') } }\n case 'spawn_agent':\n return { action: 'task_spawn', target: {} }\n case 'update_plan':\n return { action: 'todo', target: {} }\n case 'view_image':\n return { action: 'file_read', target: {} }\n default:\n return { action: 'other', target: {} }\n }\n}\n\n/** Files touched by an apply_patch, from its `*** Add/Update/Delete File:` + `*** Move to:` headers. */\nfunction patchPaths(patch: string): string[] | undefined {\n const paths: string[] = []\n for (const line of patch.split('\\n')) {\n const m = /^\\*\\*\\* (?:Add|Update|Delete) File: (.+)$/.exec(line) ?? /^\\*\\*\\* Move to: (.+)$/.exec(line)\n if (m && m[1]) paths.push(m[1].trim())\n }\n return paths.length ? paths : undefined\n}\n","import { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { registerAdapter } from '../../core/registry'\nimport type { Session } from '../../core/model'\nimport type { SourceAdapter } from '../types'\nimport { dbPathFor, openOpencodeDb } from './db'\nimport { buildSessions, PARSE_VERSION, SOURCE } from './parse'\n\n/**\n * OpenCode adapter. OpenCode keeps every session in a single `opencode.db`\n * (SQLite, since v1.2.0), so this is a store-backed adapter: it implements\n * `discoverSessions` rather than the file-oriented discover/parse pair. A root\n * without an `opencode.db` is simply skipped (the user just doesn't use OpenCode).\n */\nexport const opencodeAdapter: SourceAdapter = {\n id: SOURCE,\n provider: SOURCE,\n parseVersion: PARSE_VERSION,\n defaultRoots: () => [join(homedir(), '.local', 'share', 'opencode')],\n // Unused for store-backed adapters, but required by the interface.\n discover: async () => [],\n parse: async () => null,\n discoverSessions: async (roots) => {\n const out: Session[] = []\n for (const root of roots) {\n const path = dbPathFor(root)\n if (!path) continue\n const db = openOpencodeDb(path)\n try {\n out.push(...buildSessions(db, path))\n } finally {\n db.close()\n }\n }\n return out\n },\n}\n\nregisterAdapter(opencodeAdapter)\n","import { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport Database from 'better-sqlite3'\n\n/**\n * Read-only access to OpenCode's SQLite store. Since v1.2.0 (~Feb 2026) OpenCode\n * keeps all sessions in one `opencode.db` under its data dir (default\n * `~/.local/share/opencode`), not one file per session — so the adapter reads\n * sessions out of the DB rather than walking files. We open the DB read-only and\n * query-only so we never contend with OpenCode's live connection / WAL.\n */\n\nexport const DB_FILENAME = 'opencode.db'\n\n/** A row from the `session` table, with the workspace branch joined in. */\nexport interface OcSession {\n id: string\n parent_id: string | null\n directory: string\n title: string\n agent: string | null\n model: string | null\n version: string\n cost: number\n tokens_input: number\n tokens_output: number\n tokens_reasoning: number\n tokens_cache_read: number\n tokens_cache_write: number\n time_created: number\n time_updated: number\n branch: string | null\n}\n\n/** A row from the `message` table; `data` is a JSON blob (role, tokens, model, …). */\nexport interface OcMessage {\n id: string\n session_id: string\n time_created: number\n data: string\n}\n\n/** A row from the `part` table; `data` is a JSON blob (type, tool, state, …). */\nexport interface OcPart {\n id: string\n message_id: string\n session_id: string\n time_created: number\n data: string\n}\n\nexport type OcDb = Database.Database\n\n/** Resolve the `opencode.db` path under a data-dir root, or null if absent. */\nexport function dbPathFor(root: string): string | null {\n const p = join(root, DB_FILENAME)\n return existsSync(p) ? p : null\n}\n\n/** Open an OpenCode DB read-only. Caller closes it. */\nexport function openOpencodeDb(path: string): OcDb {\n const db = new Database(path, { readonly: true, fileMustExist: true })\n db.pragma('query_only = true')\n return db\n}\n\n/** All sessions, with `branch` joined from the owning workspace. */\nexport function allSessions(db: OcDb): OcSession[] {\n return db\n .prepare(\n `SELECT s.id, s.parent_id, s.directory, s.title, s.agent, s.model, s.version,\n s.cost, s.tokens_input, s.tokens_output, s.tokens_reasoning,\n s.tokens_cache_read, s.tokens_cache_write, s.time_created, s.time_updated,\n w.branch AS branch\n FROM session s\n LEFT JOIN workspace w ON w.id = s.workspace_id\n ORDER BY s.time_created ASC`,\n )\n .all() as OcSession[]\n}\n\n/** Messages for a session, oldest first. */\nexport function messagesFor(db: OcDb, sessionId: string): OcMessage[] {\n return db\n .prepare(\n `SELECT id, session_id, time_created, data FROM message\n WHERE session_id = ? ORDER BY time_created ASC, id ASC`,\n )\n .all(sessionId) as OcMessage[]\n}\n\n/** Parts for a session, oldest first (grouped by message by the caller). */\nexport function partsFor(db: OcDb, sessionId: string): OcPart[] {\n return db\n .prepare(\n `SELECT id, message_id, session_id, time_created, data FROM part\n WHERE session_id = ? ORDER BY time_created ASC, id ASC`,\n )\n .all(sessionId) as OcPart[]\n}\n","import type { CanonicalAction } from '../../core/model'\n\n/**\n * Map OpenCode tool names to canonical actions. This is the ONE place that knows\n * OpenCode's tool vocabulary — common extractors stay vendor-neutral. OpenCode\n * tool names are lowercase (`bash`, `read`, `edit`, …); MCP tools are namespaced\n * `<server>_<tool>` but we can't reliably distinguish those from built-ins by\n * name, so only the known built-ins are mapped and the rest fall through.\n */\n// OpenCode's registered file-mutating tool ids: `write` ({filePath,content}),\n// `edit` ({filePath,oldString,newString}), and `apply_patch` ({patchText} in the\n// Codex `*** Begin Patch` format). `apply_patch` is mutually exclusive with edit/write\n// and only enabled for gpt-5-class models (registry `usePatch` gate); a MultiEditTool\n// exists in the source but is registered nowhere, so `multiedit` is intentionally omitted.\nconst FILE_WRITE = new Set(['write', 'edit', 'apply_patch'])\nconst FILE_READ = new Set(['read'])\nconst SEARCH = new Set(['grep', 'glob', 'list'])\nconst WEB = new Set(['webfetch', 'websearch'])\nconst SHELL = new Set(['bash'])\nconst TODO = new Set(['todowrite', 'todoread'])\n\nexport interface MappedAction {\n action: CanonicalAction\n target: { paths?: string[]; command?: string }\n /** Refined identity for the tool-call `name` (e.g. the specific skill). */\n name?: string\n}\n\nexport function mapAction(name: string, input: unknown): MappedAction {\n const obj = (input && typeof input === 'object' ? input : {}) as Record<string, unknown>\n const tool = name.toLowerCase()\n if (FILE_WRITE.has(tool)) return { action: 'file_write', target: { paths: filePaths(obj) } }\n if (FILE_READ.has(tool)) return { action: 'file_read', target: { paths: filePaths(obj) } }\n if (SHELL.has(tool)) {\n return { action: 'shell', target: { command: typeof obj.command === 'string' ? obj.command : undefined } }\n }\n if (SEARCH.has(tool)) return { action: 'search', target: {} }\n if (tool === 'task') return { action: 'task_spawn', target: {} }\n if (TODO.has(tool)) return { action: 'todo', target: {} }\n if (tool === 'skill') {\n const skill = typeof obj.name === 'string' ? obj.name : typeof obj.skill === 'string' ? obj.skill : undefined\n return { action: 'skill', target: {}, name: skill }\n }\n if (WEB.has(tool)) return { action: 'web', target: {} }\n return { action: 'other', target: {} }\n}\n\nfunction filePaths(obj: Record<string, unknown>): string[] | undefined {\n const p = obj.filePath ?? obj.file_path ?? obj.path\n return typeof p === 'string' ? [p] : undefined\n}\n","import { contentHash } from '../../core/hash'\nimport { addUsage, emptyUsage } from '../../core/model'\nimport type {\n AssistantMessage,\n ContentBlock,\n Event,\n Session,\n SubagentMeta,\n TokenUsage,\n ToolCall,\n UserMessage,\n} from '../../core/model'\nimport { mapAction } from './actions'\nimport { allSessions, messagesFor, partsFor } from './db'\nimport type { OcDb, OcMessage, OcPart, OcSession } from './db'\n\n// Bump when ingest-time derivation changes so stored sessions are rebuilt on the\n// same DB rows (see analyze.ts's re-ingest gate). 1: initial OpenCode support.\nexport const PARSE_VERSION = 1\nexport const SOURCE = 'opencode'\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ntype Raw = any\n\n/**\n * Read every session out of an OpenCode DB and normalize it. Subagent sessions\n * (rows with a `parent_id`) are folded into their parent — their token usage and\n * tool calls roll up as sidechains, mirroring how Claude Code handles its\n * sidechain transcripts — so only top-level sessions are returned.\n */\nexport function buildSessions(db: OcDb, dbPath: string): Session[] {\n const sessions = allSessions(db)\n const byId = new Map(sessions.map((s) => [s.id, s]))\n const childrenByParent = new Map<string, OcSession[]>()\n for (const s of sessions) {\n if (s.parent_id && byId.has(s.parent_id)) {\n const arr = childrenByParent.get(s.parent_id)\n if (arr) arr.push(s)\n else childrenByParent.set(s.parent_id, [s])\n }\n }\n\n const out: Session[] = []\n for (const s of sessions) {\n // Folded into a parent below; don't emit standalone. Orphans (parent missing)\n // fall through and are emitted as top-level.\n if (s.parent_id && byId.has(s.parent_id)) continue\n out.push(buildTopLevel(db, dbPath, s, childrenByParent.get(s.id) ?? []))\n }\n return out\n}\n\n/** A session's events/tool-calls/usage, plus the bits needed to fold children. */\ninterface Unit {\n events: Event[]\n toolCalls: ToolCall[]\n tokens: TokenUsage\n models: Set<string>\n providers: string[]\n cwd?: string\n /** Spawning `task` calls, for linking child sessions back to their tool call. */\n taskCalls: { description: string; subagentType?: string; callID: string }[]\n}\n\nfunction buildTopLevel(db: OcDb, dbPath: string, s: OcSession, children: OcSession[]): Session {\n const parentMsgs = messagesFor(db, s.id)\n const parentParts = partsFor(db, s.id)\n const unit = buildUnit(parentMsgs, parentParts, false, undefined)\n\n const events = [...unit.events]\n const toolCalls = [...unit.toolCalls]\n let tokens = unit.tokens\n const models = new Set(unit.models)\n const sModel = modelIdOf(s.model)\n if (sModel) models.add(sModel)\n const subagents: SubagentMeta[] = []\n\n // Stable hash input across the whole logical session (parent + children rows).\n const hashParts: string[] = [s.id, String(s.time_updated)]\n appendHash(hashParts, parentMsgs, parentParts)\n\n for (const child of children) {\n const childMsgs = messagesFor(db, child.id)\n const childParts = partsFor(db, child.id)\n const cu = buildUnit(childMsgs, childParts, true, child.id)\n events.push(...cu.events)\n toolCalls.push(...cu.toolCalls)\n tokens = addUsage(tokens, cu.tokens)\n for (const m of cu.models) models.add(m)\n const childModel = modelIdOf(child.model)\n if (childModel) models.add(childModel)\n // Link the child to the parent `task` call that spawned it: OpenCode doesn't\n // store the child's id on the call, but the child's title is the call's\n // description plus a \"(@type subagent)\" suffix, so prefix-match on that.\n const spawn = unit.taskCalls.find(\n (t) => t.description && child.title.startsWith(t.description) && (!t.subagentType || t.subagentType === child.agent),\n )\n subagents.push({\n agentId: child.id,\n agentType: child.agent ?? undefined,\n description: child.title,\n toolUseId: spawn?.callID,\n })\n appendHash(hashParts, childMsgs, childParts)\n }\n\n const provider = unit.providers[0] ?? SOURCE\n const cwd = unit.cwd ?? s.directory\n\n return {\n id: `${SOURCE}:${s.id}`,\n sessionId: s.id,\n source: SOURCE,\n provider,\n title: s.title || undefined,\n project: { cwd, branch: s.branch ?? undefined },\n startedAt: iso(s.time_created),\n endedAt: iso(s.time_updated),\n models: [...models],\n tokens,\n events,\n toolCalls,\n subagents: subagents.length ? subagents : undefined,\n raw: { path: `${dbPath}#${s.id}`, contentHash: contentHash(hashParts.join('\\n')) },\n }\n}\n\nfunction buildUnit(messages: OcMessage[], parts: OcPart[], isSidechain: boolean, agentId: string | undefined): Unit {\n const partsByMsg = new Map<string, OcPart[]>()\n for (const p of parts) {\n const arr = partsByMsg.get(p.message_id)\n if (arr) arr.push(p)\n else partsByMsg.set(p.message_id, [p])\n }\n\n const events: Event[] = []\n const toolCalls: ToolCall[] = []\n const models = new Set<string>()\n const providers: string[] = []\n const taskCalls: Unit['taskCalls'] = []\n let tokens = emptyUsage()\n let cwd: string | undefined\n\n for (const msg of messages) {\n const d = parseJson(msg.data)\n if (!d) continue\n const role = d.role\n const ts = iso(d?.time?.created ?? msg.time_created)\n const cwdHere = d?.path?.cwd\n if (typeof cwdHere === 'string') cwd = cwdHere\n const msgParts = partsByMsg.get(msg.id) ?? []\n\n if (role === 'assistant') {\n const model: string | undefined = typeof d.modelID === 'string' ? d.modelID : undefined\n if (model) models.add(model)\n const provider: string | undefined = typeof d.providerID === 'string' ? d.providerID : undefined\n if (provider && !providers.includes(provider)) providers.push(provider)\n const usage = usageOf(d.tokens)\n tokens = addUsage(tokens, usage)\n\n const blocks = blocksFrom(msgParts, toolCalls, taskCalls, isSidechain, ts)\n const ev: AssistantMessage = {\n kind: 'assistant',\n ts,\n isSidechain,\n agentId,\n model,\n blocks,\n usage,\n costUsd: typeof d.cost === 'number' ? d.cost : undefined,\n }\n events.push(ev)\n } else if (role === 'user') {\n const blocks = blocksFrom(msgParts, toolCalls, taskCalls, isSidechain, ts)\n const text = blocks\n .filter((b): b is Extract<ContentBlock, { type: 'text' }> => b.type === 'text')\n .map((b) => b.text)\n .join('\\n')\n const ev: UserMessage = { kind: 'user', ts, isSidechain, agentId, text, blocks }\n events.push(ev)\n }\n }\n\n return { events, toolCalls, tokens, models, providers, cwd, taskCalls }\n}\n\n/** Turn a message's parts into content blocks, accumulating tool calls as a side effect. */\nfunction blocksFrom(\n parts: OcPart[],\n toolCalls: ToolCall[],\n taskCalls: Unit['taskCalls'],\n isSidechain: boolean,\n ts: string | undefined,\n): ContentBlock[] {\n const blocks: ContentBlock[] = []\n for (const p of parts) {\n const d = parseJson(p.data)\n if (!d) continue\n if (d.type === 'text') {\n blocks.push({ type: 'text', text: String(d.text ?? '') })\n } else if (d.type === 'reasoning') {\n blocks.push({ type: 'thinking', text: String(d.text ?? '') })\n } else if (d.type === 'tool' && typeof d.callID === 'string') {\n const tool = String(d.tool ?? '')\n const state = (d.state && typeof d.state === 'object' ? d.state : {}) as Raw\n const input = state.input\n const mapped = mapAction(tool, input)\n blocks.push({ type: 'tool_use', id: d.callID, name: tool, input })\n const isError = state.status === 'error'\n toolCalls.push({\n id: d.callID,\n name: mapped.name ?? tool,\n action: mapped.action,\n input,\n target: mapped.target,\n result: { ok: !isError, isError, raw: state.output ?? state.error ?? state.metadata },\n isSidechain,\n ts,\n durationMs: durationOf(state),\n })\n if (tool === 'task' && input && typeof input === 'object') {\n const i = input as Raw\n taskCalls.push({\n description: typeof i.description === 'string' ? i.description : '',\n subagentType: typeof i.subagent_type === 'string' ? i.subagent_type : undefined,\n callID: d.callID,\n })\n }\n }\n // step-start / step-finish carry no transcript content; ignore.\n }\n return blocks\n}\n\nfunction appendHash(into: string[], messages: OcMessage[], parts: OcPart[]): void {\n for (const m of messages) into.push(m.id, m.data)\n for (const p of parts) into.push(p.id, p.data)\n}\n\n/**\n * OpenCode tokens: { input, output, reasoning, cache: { read, write } }, where\n * reasoning is counted separately from output. tuneloop has no reasoning field, so\n * fold it into output to keep the rolled-up total whole.\n */\nfunction usageOf(t: Raw): TokenUsage {\n if (!t || typeof t !== 'object') return emptyUsage()\n const cache = t.cache && typeof t.cache === 'object' ? t.cache : {}\n return {\n input: num(t.input),\n output: num(t.output) + num(t.reasoning),\n cacheCreate: num(cache.write),\n cacheRead: num(cache.read),\n }\n}\n\nfunction durationOf(state: Raw): number | undefined {\n const start = state?.time?.start\n const end = state?.time?.end\n if (typeof start === 'number' && typeof end === 'number' && end >= start) return end - start\n return undefined\n}\n\n/**\n * Extract a plain model id from the `session.model` column, which OpenCode stores\n * as a JSON object (`{\"id\",\"providerID\",\"variant\"}`) — message-level `modelID` is\n * already a plain string, so this only normalizes the session-row form.\n */\nfunction modelIdOf(raw: string | null): string | undefined {\n if (!raw) return undefined\n const t = raw.trim()\n if (t.startsWith('{')) {\n const o = parseJson(t)\n return o && typeof o.id === 'string' ? o.id : undefined\n }\n return t || undefined\n}\n\nfunction parseJson(s: string): Raw | null {\n try {\n return JSON.parse(s)\n } catch {\n return null\n }\n}\n\nfunction iso(ms: unknown): string | undefined {\n return typeof ms === 'number' && Number.isFinite(ms) ? new Date(ms).toISOString() : undefined\n}\n\nfunction num(v: unknown): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : 0\n}\n","/**\n * Helpers for telling a REAL human turn from the machinery Claude Code injects as\n * \"user\" messages. Several features need this distinction: the session digest\n * (so autonomy/intent aren't skewed by slash-command echoes) and the\n * Files-changed view (so an edit links to the prompt that actually caused it).\n */\n\n// Claude-injected \"user\" turns that aren't the human's intent: slash-command\n// echoes and their args/output (`<command-name>`, `<command-args>`,\n// `<local-command-stdout>`, `<local-command-caveat>`, …), the local-command\n// caveat preamble, skill preambles, interrupts, and tool rejections. The command\n// tags are matched generically (`<…command-*>`, opening or closing) so a new tag\n// variant doesn't silently leak through as a real prompt.\nexport const SYNTHETIC_USER_RE =\n /^Caveat: The messages below|^Base directory for this skill:|^\\[Request interrupted|<\\/?(local-)?command-[a-z]+>|The user doesn't want to proceed with this tool use/i\n\nexport function isSyntheticUser(text: string): boolean {\n return SYNTHETIC_USER_RE.test(text)\n}\n\n/** Strip injected <system-reminder> blocks (and the trailing space they leave). */\nexport function stripReminders(text: string): string {\n return text\n .replace(/<system-reminder>[\\s\\S]*?<\\/system-reminder>/gi, ' ')\n .replace(/[ \\t]+\\n/g, '\\n')\n .trim()\n}\n\n/** True when a \"user\" event is a genuine human prompt (not injected machinery). */\nexport function isRealUserText(text: string): boolean {\n const t = stripReminders(text)\n return !!t && !isSyntheticUser(t)\n}\n","/**\n * Block-level attribution (handling_long_sessions). A long session is no longer\n * one unit of work — it ships several PRs, advances several features, moves\n * through several use-cases. We split each session's MAIN thread into a\n * deterministic partition of contiguous **blocks** so cost attributes at block\n * grain instead of being charged whole-session to every artifact it touched.\n *\n * Everything here is a pure function of the normalized `Session`, so it is\n * vendor-neutral (a new harness works once its adapter produces canonical\n * actions + sidechain links — see ARCHITECTURE.md). The partition is owned by the\n * `segment-blocks` processor, but `deterministicBlocks` is shared: outcomes-git\n * (block→PR) and enrich-session (block→use_case/feature) recompute it to know\n * which block their links/labels attach to, so all agree on `idx` without\n * cross-processor store reads.\n */\nimport type { Event, Session, ToolCall } from './model'\nimport { isRealUserText, stripReminders } from './turns'\n\n/** A tool action that closes a block (cost-attribution boundary). */\nexport type BoundaryKind = 'commit' | 'pr_create' | 'pr_merge' | 'pr_review'\n\nexport interface Block {\n idx: number\n /** Inclusive main-thread seq of the block's first event. */\n startSeq: number\n /** Inclusive main-thread seq of the block's last event. */\n endSeq: number\n /** What closed the block. */\n boundaryKind: BoundaryKind | 'user_turn' | 'session_end'\n tsStart?: string\n tsEnd?: string\n}\n\n/** Block index per usage_facts.idx and per tool_calls.idx (a total partition). */\nexport interface BlockMembership {\n /** usage[k] = block idx for the k-th assistant message (== usage_facts.idx). */\n usage: number[]\n /** tool[i] = block idx for session.toolCalls[i] (== tool_calls.idx). */\n tool: number[]\n}\n\n/**\n * Version of the shared, vendor-neutral normalization applied post-parse\n * (`assignSeq` + `mergeSessions`) whose output rides the session blob. Bumped when\n * that normalization changes, so EVERY vendor's sessions re-ingest. Combined with\n * each adapter's own `parseVersion` into the stored `parse_version` (see analyze.ts).\n */\nexport const NORMALIZE_VERSION = 3\n\n/**\n * Assign a dense ordinal `seq` to every MAIN-THREAD event, in order. Sidechain\n * events get none (they're clumped per-file by merge and roll up to the block\n * that spawned them). Run once, post-merge, before ingest — `seq` then rides the\n * session blob and is the coordinate the partition is defined in.\n */\nexport function assignSeq(session: Session): void {\n let seq = 0\n for (const ev of session.events) {\n if (ev.isSidechain) {\n ev.seq = undefined\n continue\n }\n ev.seq = seq++\n }\n}\n\n/** Classify a tool call as a block-closing boundary (shared with outcomes-git). */\nexport function boundaryKind(tc: ToolCall): BoundaryKind | null {\n if (tc.action === 'shell' && typeof tc.target.command === 'string') {\n const cmd = tc.target.command\n if (/\\bgh\\s+pr\\s+merge\\b/.test(cmd)) return 'pr_merge'\n if (/\\bgh\\s+pr\\s+create\\b/.test(cmd)) return 'pr_create'\n // Posting a review closes a block too: the reading/analysis leading up to it is\n // its own unit of work, so it doesn't bleed into the next (produce-a-PR) stretch.\n if (/\\bgh\\s+pr\\s+review\\b/.test(cmd)) return 'pr_review'\n if (/\\bgit\\b[^\\n]*\\bcommit\\b/.test(cmd)) return 'commit'\n return null\n }\n if (tc.action === 'mcp_call' && /pull_request/i.test(tc.name)) {\n // Review FIRST — `create_pull_request_review` contains \"create\" but is a POSTED\n // review, not a PR creation (mirrors parsePrRefs' ordering). A review READ\n // (get/list ...review) closes nothing.\n if (/review/i.test(tc.name)) return /(?:create|submit|add)/i.test(tc.name) ? 'pr_review' : null\n if (/merge/i.test(tc.name)) return 'pr_merge'\n if (/create/i.test(tc.name)) return 'pr_create'\n }\n return null\n}\n\n/**\n * The deterministic block partition. A new block starts at session start, at each\n * real human user turn, and immediately after each commit / PR-create / PR-merge\n * tool call. Blocks are contiguous, non-overlapping, and exhaustive over the\n * main-thread seq range. Requires `assignSeq` to have run.\n */\nexport function deterministicBlocks(session: Session): Block[] {\n const main = session.events.filter((e): e is Event => !e.isSidechain && e.seq != null)\n main.sort((a, b) => (a.seq ?? 0) - (b.seq ?? 0)) // defensive; already in order\n const m = main.length\n if (m === 0) return []\n\n const idToTool = new Map<string, ToolCall>()\n for (const tc of session.toolCalls) idToTool.set(tc.id, tc)\n\n // Per main event: does it START a block (real user turn), and does a boundary\n // tool call CLOSE it (cut after)?\n const userStart: boolean[] = new Array(m).fill(false)\n const closeKind: (BoundaryKind | null)[] = new Array(m).fill(null)\n for (let i = 0; i < m; i++) {\n const ev = main[i]!\n if (ev.kind === 'user' && isRealUserText(ev.text)) userStart[i] = true\n else if (ev.kind === 'assistant') {\n let bk: BoundaryKind | null = null\n for (const b of ev.blocks) {\n if (b.type !== 'tool_use') continue\n const tc = idToTool.get(b.id)\n if (!tc) continue\n const k = boundaryKind(tc)\n if (k === 'pr_merge' || k === 'pr_create') { bk = k; break } // producing a PR dominates a same-message review/commit\n if (k === 'pr_review') bk = 'pr_review' // a review outranks a bare commit in the same message\n else if (k === 'commit' && bk !== 'pr_review') bk = 'commit'\n }\n closeKind[i] = bk\n }\n }\n\n // Block start positions (indices into `main`).\n const starts = new Set<number>([0])\n for (let i = 0; i < m; i++) {\n if (userStart[i]) starts.add(i)\n if (closeKind[i] && i + 1 < m) starts.add(i + 1)\n }\n const sorted = [...starts].sort((a, b) => a - b)\n\n const blocks: Block[] = []\n for (let s = 0; s < sorted.length; s++) {\n const startPos = sorted[s]!\n const endPos = (s + 1 < sorted.length ? sorted[s + 1]! : m) - 1\n const startEv = main[startPos]!\n const endEv = main[endPos]!\n const boundary: Block['boundaryKind'] =\n closeKind[endPos] ?? (endPos === m - 1 ? 'session_end' : 'user_turn')\n blocks.push({\n idx: blocks.length,\n startSeq: startEv.seq!,\n endSeq: endEv.seq!,\n boundaryKind: boundary,\n tsStart: startEv.ts,\n tsEnd: endEv.ts,\n })\n }\n return blocks\n}\n\n/**\n * Attribute every block to the PR it fed into: each block belongs to the nearest\n * PR-closing block at or after it (a backward fill over `closingBlockToArtifact`,\n * which maps a PR-closing block idx → its PR artifact id). Blocks after the last\n * PR belong to none. This gives cost-per-PR the FULL cost of producing a PR\n * (all the commit-bounded blocks that led up to its create/merge), not just the\n * one block the `gh pr create` sat in.\n */\nexport function attributeBlocksToPrs(\n blocks: Block[],\n closingBlockToArtifact: Map<number, string>,\n): Array<{ blockIdx: number; artifactId: string }> {\n const out: Array<{ blockIdx: number; artifactId: string }> = []\n let nextPr: string | undefined\n for (let i = blocks.length - 1; i >= 0; i--) {\n const closing = closingBlockToArtifact.get(i)\n if (closing) nextPr = closing\n if (nextPr) out.push({ blockIdx: i, artifactId: nextPr })\n }\n return out\n}\n\n/**\n * Map every usage_facts / tool_calls row to its block. Main-thread rows map by\n * seq; sidechain rows roll up to the block that holds their spawning `Task` call\n * (`agentId → SubagentMeta.toolUseId → seq`); orphan sidechain rows (workflow\n * subagents with no spawning call) fall back to the nearest block by timestamp,\n * so the partition stays exhaustive (the master Σ-invariant depends on it).\n */\nexport function blockMembership(session: Session, blocks: Block[]): BlockMembership {\n const usage: number[] = []\n const tool: number[] = []\n if (blocks.length === 0) return { usage, tool }\n\n const maxSeq = blocks[blocks.length - 1]!.endSeq\n const seqToBlock = new Array<number>(maxSeq + 1).fill(0)\n for (const b of blocks) for (let s = b.startSeq; s <= b.endSeq; s++) seqToBlock[s] = b.idx\n\n // tool_use id -> its containing main-thread seq / sidechain agentId\n const idToSeq = new Map<string, number>()\n const idToAgent = new Map<string, string>()\n for (const ev of session.events) {\n if (ev.kind !== 'assistant') continue\n for (const b of ev.blocks) {\n if (b.type !== 'tool_use') continue\n if (!ev.isSidechain && ev.seq != null) idToSeq.set(b.id, ev.seq)\n else if (ev.agentId) idToAgent.set(b.id, ev.agentId)\n }\n }\n\n // agentId -> block, via the spawning Task tool_use's seq\n const agentToBlock = new Map<string, number>()\n for (const sa of session.subagents ?? []) {\n if (!sa.toolUseId) continue\n const seq = idToSeq.get(sa.toolUseId)\n if (seq != null) agentToBlock.set(sa.agentId, seqToBlock[seq]!)\n }\n\n const nearestByTs = (ts?: string): number => {\n if (!ts) return 0\n let best = 0\n let bestTs = ''\n for (const b of blocks) {\n if (b.tsStart != null && b.tsStart <= ts && b.tsStart >= bestTs) {\n best = b.idx\n bestTs = b.tsStart\n }\n }\n return best\n }\n\n for (const ev of session.events) {\n if (ev.kind !== 'assistant') continue\n if (!ev.isSidechain && ev.seq != null) usage.push(seqToBlock[ev.seq]!)\n else usage.push((ev.agentId ? agentToBlock.get(ev.agentId) : undefined) ?? nearestByTs(ev.ts))\n }\n\n for (const tc of session.toolCalls) {\n const seq = idToSeq.get(tc.id)\n if (seq != null) tool.push(seqToBlock[seq]!)\n else {\n const agent = idToAgent.get(tc.id)\n tool.push((agent ? agentToBlock.get(agent) : undefined) ?? nearestByTs(tc.ts))\n }\n }\n\n return { usage, tool }\n}\n\n/**\n * A complete numbered digest of the blocks for the segmentation prompt — every\n * block on one line: its opening user turn (truncated) + a compact action\n * summary. NOT truncated by block count: the idx labels must map 1:1 to the\n * model's runs, so this is the one place the digest can't elide the middle.\n */\nexport function blockSpine(session: Session, blocks: Block[]): string {\n if (blocks.length === 0) return '(no blocks)'\n const { tool } = blockMembership(session, blocks)\n const toolsByBlock = new Map<number, ToolCall[]>()\n session.toolCalls.forEach((tc, i) => {\n const b = tool[i]\n if (b == null) return\n const arr = toolsByBlock.get(b) ?? []\n arr.push(tc)\n toolsByBlock.set(b, arr)\n })\n const bySeq = new Map<number, Event>()\n for (const ev of session.events) if (!ev.isSidechain && ev.seq != null) bySeq.set(ev.seq, ev)\n\n const clip = (s: string, n: number) => (s.length > n ? s.slice(0, n) + '…' : s)\n const lines: string[] = []\n for (const b of blocks) {\n let opener = ''\n for (let s = b.startSeq; s <= b.endSeq; s++) {\n const ev = bySeq.get(s)\n if (ev && ev.kind === 'user' && isRealUserText(ev.text)) {\n opener = stripReminders(ev.text).replace(/\\s+/g, ' ')\n break\n }\n }\n const tcs = toolsByBlock.get(b.idx) ?? []\n const writes = tcs.filter((t) => t.action === 'file_write').length\n const shells = tcs.filter((t) => t.action === 'shell').length\n const acts = [writes ? `${writes} file write${writes > 1 ? 's' : ''}` : '', shells ? `${shells} shell` : '']\n .filter(Boolean)\n .join(', ')\n const tag =\n b.boundaryKind === 'pr_create'\n ? ' · opened a PR'\n : b.boundaryKind === 'pr_merge'\n ? ' · merged a PR'\n : b.boundaryKind === 'pr_review'\n ? ' · reviewed a PR'\n : b.boundaryKind === 'commit'\n ? ' · git commit'\n : ''\n const head = opener ? `user: \"${clip(opener, 200)}\"` : '(continued work — no new prompt)'\n lines.push(`[${b.idx}] ${head}${acts ? ` · ${acts}` : ''}${tag}`)\n }\n return lines.join('\\n')\n}\n","import { registerProcessor } from '../core/registry'\nimport type { Processor, ProcessorContext, ProcessorResult } from '../core/processor'\nimport { blockMembership, deterministicBlocks } from '../core/blocks'\n\n/**\n * Segment a session's main thread into the deterministic block partition and map\n * every usage_facts / tool_calls row to its block (handling_long_sessions). This\n * is the substrate the read path attributes cost on; it runs without an LLM, so\n * cost-per-PR is exact on a fresh install. The partition is a pure function of\n * the normalized session (core/blocks.ts), so it's vendor-neutral.\n *\n * Owns `blocks` + `block_usage` + `block_tool`. Labels (use_case) and feature\n * links are layered on by enrich-session; PR/commit links by outcomes-git — all\n * recompute the same `deterministicBlocks` so block indices agree.\n */\nexport const segmentBlocks: Processor = {\n name: 'segment-blocks',\n version: 2,\n kind: 'static',\n run(ctx: ProcessorContext): ProcessorResult {\n const { session } = ctx\n const blocks = deterministicBlocks(session)\n if (blocks.length === 0) return {} // all-sidechain fragment / no main thread → read path falls back to session grain\n const membership = blockMembership(session, blocks)\n return {\n blocks: blocks.map((b) => ({\n idx: b.idx,\n startSeq: b.startSeq,\n endSeq: b.endSeq,\n boundaryKind: b.boundaryKind,\n tsStart: b.tsStart,\n tsEnd: b.tsEnd,\n })),\n blockUsage: membership.usage.map((blockIdx, usageIdx) => ({ usageIdx, blockIdx })),\n blockTool: membership.tool.map((blockIdx, toolIdx) => ({ toolIdx, blockIdx })),\n }\n },\n}\n\nregisterProcessor(segmentBlocks)\n","import { registerProcessor } from '../core/registry'\nimport type { Processor } from '../core/processor'\nimport type { ArtifactInput, FileIndexInput, OutcomeInput, SessionArtifactInput } from '../store/types'\n\n/**\n * Static extractor: every file the session wrote becomes a `file` artifact\n * linked to the session (for `search` + linkage), and the session gets one\n * `file_written` outcome (session-level, no artifact). Demonstrates the\n * one-file processor pattern.\n */\nexport const filesTouched: Processor = {\n name: 'files-touched',\n version: 1,\n kind: 'static',\n run(ctx) {\n const { session } = ctx\n const repoKey = session.project.repo ?? session.project.cwd ?? ''\n const paths = new Set<string>()\n for (const t of session.toolCalls) {\n if (t.action !== 'file_write') continue\n for (const p of t.target.paths ?? []) paths.add(p)\n }\n if (paths.size === 0) return {}\n\n const artifacts: ArtifactInput[] = []\n const sessionArtifacts: SessionArtifactInput[] = []\n const files: FileIndexInput[] = []\n for (const p of paths) {\n const id = `file:${repoKey}:${p}`\n artifacts.push({ id, kind: 'file', repo: session.project.repo, ident: p, source: 'transcript' })\n sessionArtifacts.push({ artifactId: id, role: 'edited', source: 'explicit' })\n files.push({ repo: session.project.repo, path: p })\n }\n const outcomes: OutcomeInput[] = [{ type: 'file_written', artifactId: null, ts: session.endedAt }]\n return { artifacts, sessionArtifacts, files, outcomes }\n },\n}\n\nregisterProcessor(filesTouched)\n","/**\n * Shared GitHub-PR helpers used by both `outcomes-git` (PRs a session created or\n * merged) and `enrich-session` (PRs a session reviewed). Keeping the detection\n * and `gh`-enrichment in one place means both processors agree on a PR's identity\n * (`pr:owner/repo:num`) and never drift on parsing rules.\n */\nimport type { Session } from '../core/model'\nimport type { ShResult } from '../core/processor'\nimport type { ArtifactInput } from '../store/types'\n\nconst PR_URL = /https:\\/\\/github\\.com\\/([^/\\s]+)\\/([^/\\s]+)\\/pull\\/(\\d+)/\n\n/**\n * create/merge = the session changed the PR; read = it only inspected the PR;\n * review = it EXPLICITLY posted a review (`gh pr review`, an MCP review tool) — a\n * deterministic, non-LLM \"this session reviewed PR X\" signal (Layer 1).\n */\nexport type PrRefKind = 'create' | 'merge' | 'read' | 'review'\n\n/** The verdict carried by an explicit review, when the command/input reveals it. */\nexport type PrVerdict = 'approved' | 'changes_requested' | 'commented'\n\nexport interface PrRef {\n /** Natural key `pr:owner/repo:num`. */\n id: string\n owner: string\n repo: string\n num: string\n /** Canonical PR URL, safe to pass to `gh pr view`. */\n url: string\n kind: PrRefKind\n /** Index into `session.toolCalls` this ref came from; -1 for a human prompt. */\n toolIndex: number\n /** For a human-pasted link (toolIndex -1): the main-thread seq of the user event it\n * appeared in, so a caller can map it to the block that turn opened. Unset for tool refs. */\n atSeq?: number\n /** Only set for kind 'review': approve / request-changes / comment. */\n verdict?: PrVerdict\n}\n\n// gh verbs that MUTATE a PR (the only thing outcomes-git attributes as created).\nconst CREATE_VERB = /\\bgh\\s+pr\\s+create\\b/\nconst MERGE_VERB = /\\bgh\\s+pr\\s+merge\\b/\n// `gh pr review` = an explicit posted review (Layer 1) — handled before READ_VERB.\nconst REVIEW_VERB = /\\bgh\\s+pr\\s+review\\b/\n// gh verbs that READ/inspect a PR — an intentful \"this session looked at PR X\".\nconst READ_VERB = /\\bgh\\s+pr\\s+(?:view|diff|checkout|comment|edit|ready|close|reopen|status)\\b/\n// Pull the PR number out of a gh command: `gh pr diff 21 ...` / `gh pr review #21`.\nconst PR_NUM = /\\bgh\\s+pr\\s+(?:view|diff|checkout|comment|review|edit|ready|close|reopen|status)\\s+#?(\\d+)\\b/\nconst REPO_FLAG = /--repo[=\\s]+([^\\s/]+)\\/([^\\s]+)/\n\n/** Read the review verdict off a `gh pr review` command's flags. */\nfunction reviewVerdict(exec: string): PrVerdict {\n if (/--approve\\b/.test(exec)) return 'approved'\n if (/--request-changes\\b/.test(exec)) return 'changes_requested'\n return 'commented'\n}\n\n/** Read the review verdict off a GitHub MCP review input ({ event: 'APPROVE' | ... }). */\nfunction mcpVerdict(input: unknown): PrVerdict {\n const ev = input && typeof input === 'object' ? (input as Record<string, unknown>).event : undefined\n if (typeof ev === 'string') {\n if (/approve/i.test(ev)) return 'approved'\n if (/request[_-]?changes/i.test(ev)) return 'changes_requested'\n }\n return 'commented'\n}\n\n/**\n * Find every PR this session intentfully created, merged, or read. \"Intentful\"\n * means the PR is the TARGET of an action — a `gh pr <verb>`, a web fetch of the\n * PR URL, an MCP pull-request tool, or a PR link in a human prompt — NOT a PR URL\n * that merely floated through some unrelated tool's output (that scan caused past\n * false positives; see outcomes-git history). Only refs that resolve to a full\n * `owner/repo/num` are returned; a bare `gh pr diff 21` with no `--repo` is skipped.\n */\nexport function parsePrRefs(session: Session): PrRef[] {\n const refs: PrRef[] = []\n const pushRef = (owner: string, repo: string, num: string, kind: PrRefKind, toolIndex: number, verdict?: PrVerdict) => {\n refs.push({ id: prId(owner, repo, num), owner, repo, num, url: canonicalUrl(owner, repo, num), kind, toolIndex, ...(verdict ? { verdict } : {}) })\n }\n // Resolve a PR identity from the FIRST source that yields one (preferring tool\n // output, then the command) — mirrors the original `raw ?? exec` semantics.\n const addUrl = (kind: PrRefKind, toolIndex: number, ...sources: unknown[]): boolean => {\n for (const src of sources) {\n const url = matchPrUrl(src)\n if (!url) continue\n const m = PR_URL.exec(url)\n if (!m || !m[1] || !m[2] || !m[3]) continue\n pushRef(m[1], m[2], m[3], kind, toolIndex)\n return true\n }\n return false\n }\n // Identity from a `gh pr <verb> <num> ... --repo o/r` command (URL or num+repo).\n const fromShell = (exec: string, kind: PrRefKind, toolIndex: number, verdict?: PrVerdict): boolean => {\n const url = matchPrUrl(exec)\n if (url) {\n const m = PR_URL.exec(url)\n if (m && m[1] && m[2] && m[3]) { pushRef(m[1], m[2], m[3], kind, toolIndex, verdict); return true }\n }\n const numM = PR_NUM.exec(exec)\n const repoM = REPO_FLAG.exec(exec)\n if (numM && repoM && repoM[1] && repoM[2]) { pushRef(repoM[1], repoM[2], numM[1]!, kind, toolIndex, verdict); return true }\n return false\n }\n\n session.toolCalls.forEach((t, i) => {\n if (t.action === 'shell' && typeof t.target.command === 'string') {\n // Match against the executable skeleton so a PR command quoted inside a\n // heredoc/string literal (fixtures, doc text) isn't counted.\n const exec = stripInertRegions(t.target.command)\n if (CREATE_VERB.test(exec) || MERGE_VERB.test(exec)) {\n const kind: PrRefKind = MERGE_VERB.test(exec) ? 'merge' : 'create'\n // gh prints the new PR's URL to stdout on create; merge is often given it.\n addUrl(kind, i, t.result.raw, exec)\n } else if (REVIEW_VERB.test(exec)) {\n // Explicit posted review (Layer 1) — identity + verdict from the command.\n fromShell(exec, 'review', i, reviewVerdict(exec))\n } else if (READ_VERB.test(exec)) {\n // Identity must come from the command itself (URL, or num + --repo) — we do\n // NOT scan result output for a read, to avoid incidental-URL false positives.\n fromShell(exec, 'read', i)\n }\n } else if (t.action === 'mcp_call' && /pull_request/i.test(t.name)) {\n if (/review/i.test(t.name)) {\n // create/submit/add ...review = POSTING a review; get/list ...review = reading.\n if (/(?:create|submit|add)/i.test(t.name)) {\n const ref = resolveMcpInput(t.input)\n if (ref) refs.push({ ...ref, kind: 'review', toolIndex: i, verdict: mcpVerdict(t.input) })\n } else {\n const ref = resolveMcpInput(t.input)\n if (ref) refs.push({ ...ref, kind: 'read', toolIndex: i })\n else addUrl('read', i, t.input)\n }\n } else if (/(?:create|merge|update)/i.test(t.name)) {\n const kind: PrRefKind = /merge/i.test(t.name) ? 'merge' : 'create'\n addUrl(kind, i, t.result.raw, t.input)\n } else {\n // get / read / diff / files / comments — the PR is named in the tool input.\n const ref = resolveMcpInput(t.input)\n if (ref) refs.push({ ...ref, kind: 'read', toolIndex: i })\n else addUrl('read', i, t.input)\n }\n } else if (t.action === 'web') {\n // A web fetch's target IS its input — a PR URL there is an intentful read.\n addUrl('read', i, t.input, t.target.command)\n }\n })\n\n // A PR link a human pasted into a real prompt is an intentful read. Stamp the user\n // event's seq (atSeq) so a caller can map the ref to the block that turn opened.\n for (const ev of session.events) {\n if (ev.kind !== 'user' || ev.isSidechain) continue\n if (addUrl('read', -1, ev.text)) refs[refs.length - 1]!.atSeq = ev.seq\n }\n\n return refs\n}\n\n/** The starting artifact row for a PR, before `gh` enrichment fills in details. */\nexport function prArtifactBase(ref: PrRef): ArtifactInput {\n return {\n id: ref.id,\n kind: 'pr',\n repo: `${ref.owner}/${ref.repo}`,\n ident: ref.num,\n externalId: ref.url,\n source: 'github',\n status: 'open',\n }\n}\n\n/**\n * Fill in a PR artifact's live details via `gh pr view` (title/state/dates/churn/\n * author). Degrades gracefully: if `gh` is missing or offline the base row is\n * returned unchanged, and the store's COALESCE upsert keeps any richer row a prior\n * run already wrote — so an offline run never blanks out good data.\n */\nexport async function enrichPrArtifact(\n sh: (cmd: string, args: string[], opts?: { cwd?: string }) => Promise<ShResult | null>,\n base: ArtifactInput,\n cwd?: string,\n): Promise<ArtifactInput> {\n const art: ArtifactInput = { ...base }\n if (!art.externalId) return art\n const res = await sh('gh', ['pr', 'view', art.externalId, '--json', 'title,state,createdAt,mergedAt,additions,deletions,author'], { cwd })\n if (res && res.code === 0) {\n try {\n const j = JSON.parse(res.stdout) as {\n title?: string\n state?: string\n createdAt?: string | null\n mergedAt?: string | null\n additions?: number\n deletions?: number\n author?: { login?: string }\n }\n if (typeof j.title === 'string') art.title = j.title\n if (typeof j.state === 'string') art.status = j.state.toLowerCase()\n if (j.createdAt) art.createdAt = j.createdAt\n if (j.mergedAt) art.completedAt = j.mergedAt\n const churn = (j.additions ?? 0) + (j.deletions ?? 0)\n if (churn > 0) {\n art.complexity = churn\n art.complexityBasis = 'diff_size'\n }\n if (j.author?.login) art.owner = j.author.login\n } catch {\n /* leave base defaults */\n }\n }\n return art\n}\n\nfunction prId(owner: string, repo: string, num: string): string {\n return `pr:${owner}/${repo}:${num}`\n}\n\nfunction canonicalUrl(owner: string, repo: string, num: string): string {\n return `https://github.com/${owner}/${repo}/pull/${num}`\n}\n\nfunction matchPrUrl(raw: unknown): string | null {\n if (raw == null) return null\n const s = typeof raw === 'string' ? raw : JSON.stringify(raw)\n const m = PR_URL.exec(s)\n return m ? m[0] : null\n}\n\n/** Resolve a structured MCP pull-request input ({owner, repo, pull_number}) to a ref identity. */\nfunction resolveMcpInput(input: unknown): Omit<PrRef, 'kind' | 'toolIndex'> | null {\n if (!input || typeof input !== 'object') return null\n const o = input as Record<string, unknown>\n const owner = typeof o.owner === 'string' ? o.owner : null\n const repo = typeof o.repo === 'string' ? o.repo : null\n const numRaw = o.pull_number ?? o.pullNumber ?? o.number ?? o.pr\n const num = typeof numRaw === 'number' ? String(numRaw) : typeof numRaw === 'string' && /^\\d+$/.test(numRaw) ? numRaw : null\n if (!owner || !repo || !num) return null\n return { id: prId(owner, repo, num), owner, repo, num, url: canonicalUrl(owner, repo, num) }\n}\n\n// Sinks that execute their heredoc body; for any other sink (cat, tee, a file\n// redirect) the body is inert data and gets stripped.\nconst HEREDOC_INTERPRETER = /\\b(?:bash|sh|zsh|ksh|dash|python3?|node|ruby|perl|php|fish|eval|source)\\b/\nconst HEREDOC_START = /<<-?\\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\\1/\n\n/**\n * Strip the parts of a shell command the shell would never run as a command —\n * non-executing heredoc bodies and quoted string literals — leaving an\n * \"executable skeleton\" to match against. Best-effort heuristic, not a parser.\n */\nexport function stripInertRegions(cmd: string): string {\n const lines = cmd.split('\\n')\n const out: string[] = []\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? ''\n const m = HEREDOC_START.exec(line)\n if (!m || m.index === undefined) {\n out.push(line)\n continue\n }\n const delim = m[2]\n const dashed = line.slice(m.index, m.index + 3).includes('<<-')\n const executes = HEREDOC_INTERPRETER.test(line.slice(0, m.index))\n out.push(line) // introducing line is itself a real command\n let j = i + 1\n for (; j < lines.length; j++) {\n const body = lines[j] ?? ''\n if ((dashed ? body.replace(/^\\t+/, '') : body) === delim) break\n if (executes) out.push(body)\n }\n if (j < lines.length) out.push(lines[j] ?? '') // closing delimiter\n i = j\n }\n // Blank quoted literals (keeping token boundaries); after heredocs so quoted\n // delimiters above still match.\n return out\n .join('\\n')\n .replace(/'[^']*'/g, \"''\")\n .replace(/\"(?:\\\\.|[^\"\\\\])*\"/g, '\"\"')\n}\n","import { registerProcessor } from '../core/registry'\nimport { attributeBlocksToPrs, blockMembership, deterministicBlocks } from '../core/blocks'\nimport type { Processor, RefreshContext, RefreshResult } from '../core/processor'\nimport type { ArtifactInput, OutcomeInput, SessionArtifactInput, BlockArtifactInput } from '../store/types'\nimport { enrichPrArtifact, parsePrRefs, prArtifactBase, stripInertRegions } from './github-pr'\nimport type { PrVerdict } from './github-pr'\n\n// Re-exported so existing tests (and any importer) keep their import path stable.\nexport { stripInertRegions } from './github-pr'\n\n/**\n * Static + network extractor: detect commits and PRs from the transcript, then\n * query `gh` for live PR status (degrades gracefully when offline / gh missing).\n * A commit with no resolvable SHA yields a session-level `commit_pushed` outcome\n * with no artifact — the nullable-artifact case from the data model.\n *\n * Also detects EXPLICIT reviews (Layer 1): a `gh pr review` / GitHub MCP review\n * tool is a deterministic \"this session reviewed PR X\" — linked here as\n * `reviewed` (source='explicit', confidence 1.0) with a verdict outcome. (The\n * softer, LLM-derived Layer 2 lives in `enrich-session`; it defers to this one.)\n * PRs a session only READ — without an explicit review — are still left to Layer 2.\n */\nexport const outcomesGit: Processor = {\n name: 'outcomes-git',\n version: 5,\n kind: 'static',\n needs: { network: true },\n requires: ['segment-blocks'],\n async run(ctx) {\n const { session, sh } = ctx\n const cwd = session.project.cwd\n const artifacts: ArtifactInput[] = []\n const sessionArtifacts: SessionArtifactInput[] = []\n const outcomes: OutcomeInput[] = []\n\n // A commit with no resolvable SHA: a session-level outcome with no artifact.\n let committed = false\n for (const t of session.toolCalls) {\n if (t.action === 'shell' && typeof t.target.command === 'string') {\n // Match the executable skeleton so fixture/doc text isn't counted.\n if (/\\bgit\\b[^\\n]*\\bcommit\\b/.test(stripInertRegions(t.target.command))) {\n committed = true\n break\n }\n }\n }\n if (committed) outcomes.push({ type: 'commit_pushed', artifactId: null, ts: session.endedAt })\n\n // Only attribute PRs this session actually created/merged — NOT ones it merely\n // read (those are handled, gated on the review use-case, by enrich-session).\n const refs = parsePrRefs(session)\n const mutating = refs.filter((r) => r.kind === 'create' || r.kind === 'merge')\n const byId = new Map<string, (typeof mutating)[number]>()\n for (const r of mutating) if (!byId.has(r.id)) byId.set(r.id, r)\n\n for (const ref of byId.values()) {\n const art = await enrichPrArtifact(sh, prArtifactBase(ref), cwd)\n artifacts.push(art)\n sessionArtifacts.push({ artifactId: ref.id, role: 'created', source: 'explicit' })\n outcomes.push({ type: 'pr_created', artifactId: ref.id, ts: session.endedAt })\n if (art.status === 'merged' || art.completedAt) {\n outcomes.push({ type: 'pr_merged', artifactId: ref.id, ts: art.completedAt })\n }\n }\n\n const blocks = deterministicBlocks(session)\n const tool = blocks.length ? blockMembership(session, blocks).tool : []\n const blockArtifacts: BlockArtifactInput[] = []\n\n // Layer 1 — EXPLICIT reviews (`gh pr review` / MCP review tool). Deterministic, so\n // the link is source='explicit', confidence 1.0. Self-created PRs are excluded (you\n // don't \"review\" your own PR here). enrich-session's derived Layer 2 skips any PR\n // linked here. Block-grain cost is assigned below, alongside created PRs.\n const mutatingIds = new Set(mutating.map((r) => r.id))\n const reviewed = new Map<string, { ref: (typeof refs)[number]; verdict?: PrVerdict }>()\n for (const r of refs) {\n if (r.kind !== 'review' || mutatingIds.has(r.id)) continue\n const prev = reviewed.get(r.id)\n // Keep a decisive verdict (approved / changes_requested) over a bare comment.\n if (!prev || (rankVerdict(r.verdict) > rankVerdict(prev.verdict))) reviewed.set(r.id, { ref: r, verdict: r.verdict })\n }\n for (const [id, { ref, verdict }] of reviewed) {\n artifacts.push(await enrichPrArtifact(sh, prArtifactBase(ref), cwd))\n sessionArtifacts.push({ artifactId: id, role: 'reviewed', source: 'explicit', confidence: 1 })\n outcomes.push({ type: 'pr_reviewed', artifactId: id, ts: session.endedAt })\n if (verdict === 'approved') outcomes.push({ type: 'pr_approved', artifactId: id, ts: session.endedAt })\n else if (verdict === 'changes_requested') outcomes.push({ type: 'pr_changes_requested', artifactId: id, ts: session.endedAt })\n }\n\n // Block→PR — ONE backward-fill over both event kinds, so every block belongs to\n // exactly one PR under one role (1-1): each block is attributed to the NEXT event\n // at/after it — `gh pr create`/`merge` → `contributed` (the work producing the PR,\n // incl. the blocks leading up to it); `gh pr review` → `reviewed` (the reading/\n // analysis leading up to posting the review). Because `gh pr review` is now a block\n // boundary (deterministicBlocks), a review and a later create never collapse into\n // one block. A create/merge + review in the SAME message go to production (create/\n // merge dominate). Blocks after the last event stay unattributed, as for create-only.\n if (blocks.length) {\n const anchors = new Map<number, string>()\n const roleOf = new Map<string, 'contributed' | 'reviewed'>()\n for (const ref of mutating) {\n const bi = tool[ref.toolIndex]\n if (bi != null) { anchors.set(bi, ref.id); roleOf.set(ref.id, 'contributed') }\n }\n for (const [id, { ref }] of reviewed) {\n const bi = tool[ref.toolIndex]\n if (bi != null && !anchors.has(bi)) { anchors.set(bi, id); roleOf.set(id, 'reviewed') } // create/merge on a shared block wins\n }\n for (const { blockIdx, artifactId } of attributeBlocksToPrs(blocks, anchors)) {\n const role = roleOf.get(artifactId)!\n blockArtifacts.push({ blockIdx, artifactId, role, source: 'explicit', ...(role === 'reviewed' ? { confidence: 1 } : {}) })\n }\n }\n\n return { artifacts, sessionArtifacts, outcomes, blockArtifacts }\n },\n\n async refresh(ctx: RefreshContext): Promise<RefreshResult> {\n const { artifacts: stale, sh, log } = ctx\n const updated: ArtifactInput[] = []\n const outcomes: OutcomeInput[] = []\n\n for (const art of stale) {\n if (art.kind !== 'pr' || !art.externalId) continue\n const res = await sh('gh', ['pr', 'view', art.externalId, '--json', 'state,mergedAt,additions,deletions'], {})\n if (!res || res.code !== 0) continue\n try {\n const j = JSON.parse(res.stdout) as { state?: string; mergedAt?: string | null; additions?: number; deletions?: number }\n const status = j.state?.toLowerCase()\n const churn = (j.additions ?? 0) + (j.deletions ?? 0)\n const needsComplexity = !art.complexity && churn > 0\n if (!needsComplexity && (!status || status === art.status)) continue\n log.debug(`refresh: ${art.externalId} ${art.status} → ${status}`)\n const patch: ArtifactInput = { ...art, status: status ?? art.status, completedAt: j.mergedAt ?? art.completedAt }\n if (needsComplexity) {\n patch.complexity = churn\n patch.complexityBasis = 'diff_size'\n }\n updated.push(patch)\n if (status === 'merged' && j.mergedAt) {\n outcomes.push({ type: 'pr_merged', artifactId: art.id, ts: j.mergedAt })\n }\n } catch { /* skip unparseable */ }\n }\n\n return { artifacts: updated, outcomes }\n },\n}\n\n/** A decisive verdict (approved / changes_requested) outranks a bare comment. */\nfunction rankVerdict(v?: PrVerdict): number {\n return v && v !== 'commented' ? 1 : 0\n}\n\nregisterProcessor(outcomesGit)\n","/**\n * Content-match PR linkage (the \"diff-match\" inferred link). Recovers the common\n * workflow where the user asks the agent to implement something, then commits and\n * pushes it themselves — so no `gh pr create` is in the transcript and `outcomes-git`\n * never links the session to the shipped PR.\n *\n * Matches the lines the agent authored (Claude Edit/Write/MultiEdit; Codex & OpenCode\n * apply_patch; OpenCode write/edit) against each candidate PR's net added lines\n * (`gh pr diff`) by LINE-LEVEL CONTAINMENT anchored on the PR: the fraction of the PR's\n * added lines the session authored. That fraction is the PR's AI-attribution, stored as\n * the link `confidence`. Design + the bake-off behind it: docs/plans/pr-linkage.md.\n *\n * - Author-scoped: only the user's OWN PRs (`gh pr list --author @me`) are candidates —\n * every eval false positive was a teammate's PR sharing foundational code, so scoping\n * that class out makes a low threshold safe.\n * - Attribution is measured for every contributed PR, including ones the session created\n * itself; for those `outcomes-git` owns the cost/outcome rows, so only the attribution\n * link is emitted here (see the run loop).\n * - Link is `source:'derived'` (user-rejectable) with `role:'edited'`; the producer\n * column marks it content-matched.\n */\nimport { basename, isAbsolute, resolve } from 'node:path'\nimport { registerProcessor } from '../core/registry'\nimport { deterministicBlocks, blockMembership, attributeBlocksToPrs } from '../core/blocks'\nimport type { Session, ToolCall } from '../core/model'\nimport type { Processor, ProcessorContext, RefreshContext, RefreshResult, ShResult } from '../core/processor'\nimport type { ArtifactInput, BlockArtifactInput, OutcomeInput, SessionArtifactInput } from '../store/types'\nimport { parsePrRefs } from './github-pr'\n\n/** Inferred-link precision gate: the session must author ≥ this fraction of the PR. */\nconst CONF_THRESHOLD = 0.1\n/** Minimum matched lines before we trust the fraction at all — a ratio computed from\n * 1–2 lines is noise (\"100%\" of a tiny diff can be pure coincidence). This is the\n * evidence floor; CONF_THRESHOLD does the proportional work for larger PRs. */\nconst MIN_MATCHED = 3\n/** Machine-generated files nobody authors — excluded from the attribution fraction\n * entirely (a lockfile regenerated by `npm install` would deflate the %). */\nconst GENERATED_FILES = new Set([\n 'package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lock', 'bun.lockb',\n 'Cargo.lock', 'poetry.lock', 'uv.lock', 'Pipfile.lock', 'Gemfile.lock', 'composer.lock', 'go.sum',\n])\n\ntype Sh = ProcessorContext['sh']\n\ninterface CandidatePr {\n id: string\n num: string\n url: string\n art: ArtifactInput\n /** Added/removed lines per repo-relative path (normalized + de-trivialized at match\n * time), plus the file's full pre-PR content when its base blob is available locally\n * (`base: null` = unavailable → the removed-lines rule is the fallback). */\n files: { path: string; added: string[]; removed: string[]; base: string[] | null }[]\n}\n\n// Candidate PRs are the same for every session in a repo, so fetch once per repo per\n// process (keyed owner/repo). Mirrors loadOpenRouterPrices' process-level cache.\nconst prCacheByRepo = new Map<string, Promise<CandidatePr[] | null>>()\n/** Test seam: drop the per-repo PR cache. */\nexport function __resetPrCache(): void {\n prCacheByRepo.clear()\n}\n\nexport const prContentMatch: Processor = {\n name: 'pr-content-match',\n version: 5,\n kind: 'static',\n needs: { network: true },\n // outcomes-git is a hard dep for WRITE ORDERING, not for input: its block rows\n // must already be persisted when this processor persists, so our evidence-based\n // fill can physically displace its proximity fill for the same block (1-1 table,\n // no read-time reconciliation — see store.persistResult). Bump this version\n // whenever outcomes-git's block attribution changes, so the displace re-fires.\n requires: ['segment-blocks', 'outcomes-git'],\n async run(ctx) {\n const { session, sh } = ctx\n const cwd = session.project.cwd\n // Cheap pre-checks: nothing to match without authored edits or a working dir.\n if (!cwd || !session.toolCalls.some((t) => t.action === 'file_write')) return {}\n\n const repoRoot = await gitToplevel(sh, cwd)\n const ownerRepo = await gitOwnerRepo(sh, cwd)\n if (!repoRoot || !ownerRepo) return {}\n\n const authored = authoredByFile(session, cwd, repoRoot)\n if (authored.size === 0) return {}\n\n const candidates = await candidatePrs(sh, ownerRepo, repoRoot)\n // gh INFRA failure (vs a repo with genuinely no candidate PRs): throw so the runner skips persisting\n if (candidates === null) throw new Error(`gh unavailable for ${ownerRepo}; keeping prior content-match results`)\n if (candidates.length === 0) return {}\n\n // PRs the session explicitly created/merged: outcomes-git owns their cost/outcome\n // rows, so only their attribution link is emitted below. Keys are lowercased because\n // GitHub owner/repo are case-insensitive — parsePrRefs (transcript casing) and our\n // candidate id (git-remote casing) can skew, which must not make a self-created PR\n // look inferred (that would double-emit outcomes-git's rows).\n const mutatingRefs = parsePrRefs(session).filter((r) => r.kind === 'create' || r.kind === 'merge')\n const explicit = new Set(mutatingRefs.map((r) => r.id.toLowerCase()))\n\n const blocks = deterministicBlocks(session)\n const toolToBlock = blocks.length ? blockMembership(session, blocks).tool : []\n\n const artifacts: ArtifactInput[] = []\n const sessionArtifacts: SessionArtifactInput[] = []\n const outcomes: OutcomeInput[] = []\n // Inferred PRs that passed the gates, with the blocks whose tool calls matched them —\n // inputs to the unified block→PR fill after the loop.\n const inferredMatches: Array<{ id: string; confidence: number; blocks: Set<number> }> = []\n\n for (const pr of candidates) {\n // A PR that merged before the session started cannot contain the session's code.\n // Only excludes on a valid, strictly-earlier merge time (open/unmerged PRs stay).\n if (shippedBeforeSession(pr, session.startedAt)) continue\n const inferred = !explicit.has(pr.id.toLowerCase())\n let total = 0\n let matched = 0\n const matchedToolIdxs = new Set<number>()\n for (const file of pr.files) {\n if (GENERATED_FILES.has(basename(file.path))) continue\n const auth = authored.get(file.path)\n // Net-new only: a line that already existed in the file BEFORE this PR (moved,\n // re-indented, duplicated, reverted) is not new content — matching it would credit\n // whoever ORIGINALLY authored it. Base file when available locally; the\n // diff's own removed lines otherwise (a subset of the same rule)\n const excluded = new Set(meaningful(file.removed))\n if (file.base) for (const l of meaningful(file.base)) excluded.add(l)\n const prUnique = new Set(meaningful(file.added).filter((l) => !excluded.has(l)))\n total += prUnique.size\n if (!auth) continue\n for (const line of prUnique) {\n const tls = auth.lines.get(line)\n if (!tls) continue\n matched++\n // Only the tool calls that authored THIS matched line mark their blocks.\n for (const idx of tls) matchedToolIdxs.add(idx)\n }\n }\n // Evidence floor: need at least a few real matched lines before trusting the ratio.\n if (total === 0 || matched < MIN_MATCHED) continue\n const confidence = matched / total\n // Proportional PRECISION gate for INFERRED links (a big PR with few matched lines\n // is weak). For a PR the session explicitly created the contribution is already\n // certain, so we record its attribution even when the fraction is low.\n if (inferred && confidence < CONF_THRESHOLD) continue\n\n // Recorded for every contributed PR (created or not). `addedLines` makes `matched`\n // recoverable from confidence, for per-PR attribution summed across sessions.\n artifacts.push({ ...pr.art, json: { addedLines: total } })\n sessionArtifacts.push({ artifactId: pr.id, role: 'edited', source: 'derived', confidence })\n\n // Cost attribution (block grain) + the contributed outcome are outcomes-git's job\n // for created/merged PRs; only emit them for the NEW links we discover.\n if (inferred) {\n outcomes.push({ type: 'pr_contributed', artifactId: pr.id, ts: session.endedAt })\n const matchedBlocks = new Set<number>()\n for (const idx of matchedToolIdxs) {\n const bi = toolToBlock[idx]\n if (bi != null) matchedBlocks.add(bi)\n }\n if (matchedBlocks.size) inferredMatches.push({ id: pr.id, confidence, blocks: matchedBlocks })\n }\n }\n\n const blockArtifacts = unifiedBlockFill(blocks, toolToBlock, mutatingRefs, inferredMatches)\n\n return { artifacts, sessionArtifacts, blockArtifacts, outcomes }\n },\n\n // Keep discovered PRs' merge status current (these PRs may exist ONLY because of a\n // content match, so no other processor refreshes them). Mirrors outcomes-git.refresh.\n async refresh(ctx: RefreshContext): Promise<RefreshResult> {\n const { artifacts: stale, sh, log } = ctx\n const updated: ArtifactInput[] = []\n const outcomes: OutcomeInput[] = []\n for (const art of stale) {\n if (art.kind !== 'pr' || !art.externalId) continue\n const res = await sh('gh', ['pr', 'view', art.externalId, '--json', 'state,mergedAt'], {})\n if (!res || res.code !== 0) continue\n try {\n const j = JSON.parse(res.stdout) as { state?: string; mergedAt?: string | null }\n const status = j.state?.toLowerCase()\n if (!status || status === art.status) continue\n log.debug(`refresh: ${art.externalId} ${art.status} → ${status}`)\n updated.push({ ...art, status, completedAt: j.mergedAt ?? art.completedAt })\n if (status === 'merged' && j.mergedAt) outcomes.push({ type: 'pr_merged', artifactId: art.id, ts: j.mergedAt })\n } catch {\n /* skip unparseable */\n }\n }\n return { artifacts: updated, outcomes }\n },\n}\n\n// ---- block→PR attribution (unified backward-fill) ---------------------------\n\n/**\n * One backward-fill over both anchor kinds — explicit (the block holding a\n * `gh pr create`/`merge`, as outcomes-git) and synthetic (an inferred PR's last\n * corroborated matched block) — so each block belongs to exactly one PR, and a\n * human-pushed PR reclaims the blocks outcomes-git's explicit-only fill would\n * absorb into the next created PR.\n *\n * Contested block: explicit wins (ground truth); between inferred PRs higher\n * confidence wins, the loser falls back to an earlier matched block. A PR contested\n * out entirely gets no block rows and thus NO cost claim (the store gates the\n * whole-session fallback off for this producer — saNoContentMatchFallback);\n * its attribution % on the session link stands. Under-claim over over-claim.\n *\n * Only inferred-owned segments are emitted; explicit segments stay outcomes-git's.\n * Where the fills disagree, our row physically DISPLACES outcomes-git's proximity\n * row for that block at write time (store.persistResult), keeping block_artifacts\n * 1-1 — no read-time reconciliation.\n * Block confidence is NULL as in outcomes-git: attribution % lives on the session link.\n */\nfunction unifiedBlockFill(\n blocks: ReturnType<typeof deterministicBlocks>,\n toolToBlock: number[],\n mutatingRefs: Array<{ id: string; toolIndex: number }>,\n inferredMatches: Array<{ id: string; confidence: number; blocks: Set<number> }>,\n): BlockArtifactInput[] {\n if (!blocks.length || !inferredMatches.length) return []\n const anchors = new Map<number, string>()\n for (const ref of mutatingRefs) {\n const bi = toolToBlock[ref.toolIndex]\n if (bi != null) anchors.set(bi, ref.id)\n }\n // Corroboration distance, scaled to session size: in a 350-block session a lone match\n // 150 blocks past the cluster is noise; in a 5-block session a gap of 2 is normal.\n const gap = Math.max(2, Math.ceil(blocks.length * 0.1))\n // Confidence order (id tiebreak for reproducibility across gh list orderings).\n for (const im of [...inferredMatches].sort((a, b) => b.confidence - a.confidence || a.id.localeCompare(b.id))) {\n const anchor = corroboratedAnchor([...im.blocks], gap)\n if (anchor < 0) continue\n // Preference: the corroborated anchor, then earlier matched blocks (each strictly\n // more conservative). Matched blocks PAST the anchor are the suspected false\n // positives — never used, not even as contested-block fallbacks.\n const prefs = [anchor, ...[...im.blocks].filter((b) => b < anchor).sort((a, b) => b - a)]\n for (const bi of prefs) {\n if (!anchors.has(bi)) {\n anchors.set(bi, im.id)\n break\n }\n }\n }\n const inferredIds = new Set(inferredMatches.map((im) => im.id))\n const out: BlockArtifactInput[] = []\n for (const { blockIdx, artifactId } of attributeBlocksToPrs(blocks, anchors)) {\n if (inferredIds.has(artifactId)) out.push({ blockIdx, artifactId, role: 'contributed', source: 'derived' })\n }\n return out\n}\n\n/**\n * Synthetic anchor: the LAST matched block whose gap to the previous one is ≤ `gap`.\n * An isolated straggler past the cluster (a shared line coinciding with a later PR's\n * segment) is rejected so it can't drag the segment end rightward. A single matched\n * block is accepted; if every pair is isolated, the earliest wins (minimal claim).\n */\nfunction corroboratedAnchor(matched: number[], gap: number): number {\n const desc = [...matched].sort((a, b) => b - a)\n if (desc.length === 0) return -1 // defensive; callers pass non-empty sets\n if (desc.length === 1) return desc[0]!\n for (let i = 0; i < desc.length - 1; i++) {\n if (desc[i]! - desc[i + 1]! <= gap) return desc[i]!\n }\n return desc[desc.length - 1]!\n}\n\n/** True only when the PR merged strictly before the session started — the one case where\n * the session provably cannot have authored any of its shipped code. Open/unmerged PRs\n * (no `completedAt`) and unknown/unparseable times are kept, so the guard never over-excludes. */\nfunction shippedBeforeSession(pr: CandidatePr, sessionStart: string | undefined): boolean {\n if (!sessionStart || !pr.art.completedAt) return false\n const merged = Date.parse(pr.art.completedAt)\n const start = Date.parse(sessionStart)\n return Number.isFinite(merged) && Number.isFinite(start) && merged < start\n}\n\n// ---- candidate PRs (author-scoped, memoized per repo) ----------------------\n\nasync function candidatePrs(sh: Sh, ownerRepo: string, repoRoot: string): Promise<CandidatePr[] | null> {\n const cached = prCacheByRepo.get(ownerRepo)\n if (cached) return cached\n // null = gh infra failure (never cached; run() throws), [] = genuinely no PRs (cached).\n const p = fetchMyPrs(sh, ownerRepo, repoRoot).then((res) => {\n if (res === null) prCacheByRepo.delete(ownerRepo)\n return res\n })\n prCacheByRepo.set(ownerRepo, p)\n return p\n}\n\ninterface PrMeta {\n number: number\n title?: string\n author?: { login?: string }\n state?: string\n createdAt?: string\n mergedAt?: string | null\n additions?: number\n deletions?: number\n}\n\nasync function fetchMyPrs(sh: Sh, ownerRepo: string, repoRoot: string): Promise<CandidatePr[] | null> {\n const list = await sh('gh', [\n 'pr', 'list', '--repo', ownerRepo, '--author', '@me', '--state', 'all', '--limit', '200',\n '--json', 'number,title,author,state,createdAt,mergedAt,additions,deletions',\n ])\n if (!list || list.code !== 0) return null // infra failure — don't cache\n let metas: PrMeta[]\n try {\n metas = JSON.parse(list.stdout)\n } catch {\n return null // unparseable gh output — treat as failure, not \"no PRs\"\n }\n const out: CandidatePr[] = []\n for (const m of metas) {\n const diff = await sh('gh', ['pr', 'diff', String(m.number), '--repo', ownerRepo])\n if (!diff || diff.code !== 0) continue\n const parsed = parseDiff(diff.stdout)\n if (parsed.length === 0) continue\n const files: CandidatePr['files'] = []\n for (const f of parsed) {\n files.push({ path: f.path, added: f.added, removed: f.removed, base: await baseContent(sh, repoRoot, f) })\n }\n out.push(toCandidate(ownerRepo, m, files))\n }\n return out\n}\n\n/**\n * The file's pre-PR content, read from the diff's own `index <old>..<new>` blob — the\n * exact base the diff was computed against, for every merge strategy (merge/squash/\n * rebase) and for open PRs (their merge-base). Local read, no network. Null (→ the\n * removed-lines fallback) when the blob isn't in this clone (shallow/unfetched);\n * `[]` for files new at base (zero old blob: everything is new content).\n */\nasync function baseContent(sh: Sh, repoRoot: string, f: ReturnType<typeof parseDiff>[number]): Promise<string[] | null> {\n // Skip files the match loop won't score anyway (lockfiles can be huge blobs).\n if (f.added.length === 0 || GENERATED_FILES.has(basename(f.path))) return null\n if (!f.oldBlob) return null\n if (/^0+$/.test(f.oldBlob)) return []\n const res = await sh('git', ['cat-file', 'blob', f.oldBlob], { cwd: repoRoot })\n return res && res.code === 0 ? res.stdout.split('\\n') : null\n}\n\nfunction toCandidate(ownerRepo: string, m: PrMeta, files: CandidatePr['files']): CandidatePr {\n const num = String(m.number)\n const id = `pr:${ownerRepo}:${num}`\n const url = `https://github.com/${ownerRepo}/pull/${num}`\n const churn = (m.additions ?? 0) + (m.deletions ?? 0)\n const art: ArtifactInput = {\n id,\n kind: 'pr',\n repo: ownerRepo,\n ident: num,\n externalId: url,\n source: 'github',\n title: m.title,\n owner: m.author?.login,\n status: m.state?.toLowerCase(),\n createdAt: m.createdAt,\n completedAt: m.mergedAt ?? undefined,\n ...(churn > 0 ? { complexity: churn, complexityBasis: 'diff_size' } : {}),\n }\n return { id, num, url, art, files }\n}\n\n/** Parse a unified diff into per-file added (`+`) and removed (`-`) lines, plus the\n * pre-image blob hash from the `index <old>..<new>` header (base-containment reads it). */\nexport function parseDiff(diff: string): { path: string; added: string[]; removed: string[]; oldBlob: string | null }[] {\n const byFile = new Map<string, { added: string[]; removed: string[]; oldBlob: string | null }>()\n let cur: { added: string[]; removed: string[]; oldBlob: string | null } | null = null\n let pendingOldBlob: string | null = null // `index` precedes `+++ b/`, so hold it until cur exists\n for (const line of diff.split('\\n')) {\n const m = line.match(/^\\+\\+\\+ b\\/(.+)$/)\n if (m) {\n cur = null\n if (m[1] !== '/dev/null') {\n cur = byFile.get(m[1]!) ?? { added: [], removed: [], oldBlob: null }\n cur.oldBlob ??= pendingOldBlob\n byFile.set(m[1]!, cur)\n }\n pendingOldBlob = null\n continue\n }\n if (line.startsWith('diff --git')) {\n cur = null // so a deleted file's (`+++ /dev/null`) lines never leak into the previous file\n pendingOldBlob = null\n continue\n }\n const idx = line.match(/^index ([0-9a-f]+)\\.\\.[0-9a-f]+/)\n if (idx) {\n pendingOldBlob = idx[1]!\n continue\n }\n if (line.startsWith('--- ') || line.startsWith('@@')) continue\n if (cur && line.startsWith('+') && !line.startsWith('+++')) cur.added.push(line.slice(1))\n else if (cur && line.startsWith('-') && !line.startsWith('---')) cur.removed.push(line.slice(1))\n }\n return [...byFile].map(([path, f]) => ({ path, ...f }))\n}\n\n// ---- session authored lines ------------------------------------------------\n\ninterface Authored {\n /** Normalized authored line → tool-call indices that authored it. Per-LINE so only\n * tool calls whose own lines match a PR mark their blocks — an unrelated later edit\n * to the same file can't manufacture a straggler \"matched block\". */\n lines: Map<string, number[]>\n}\n\nfunction authoredByFile(session: Session, cwd: string, repoRoot: string): Map<string, Authored> {\n const out = new Map<string, Authored>()\n session.toolCalls.forEach((t, i) => {\n if (t.action !== 'file_write') return\n const extracted =\n session.source === 'codex' ? codexAuthored(t) : session.source === 'opencode' ? opencodeAuthored(t) : claudeAuthored(t)\n for (const { path, lines } of extracted) {\n const rel = repoRel(path, cwd, repoRoot)\n if (!rel) continue\n let entry = out.get(rel)\n if (!entry) {\n entry = { lines: new Map() }\n out.set(rel, entry)\n }\n for (const l of meaningful(lines)) {\n let tls = entry.lines.get(l)\n if (!tls) {\n tls = []\n entry.lines.set(l, tls)\n }\n tls.push(i)\n }\n }\n })\n return out\n}\n\n/** Claude Edit/Write/MultiEdit → (path, authored lines). */\nfunction claudeAuthored(t: ToolCall): { path: string; lines: string[] }[] {\n const input = t.input as Record<string, unknown> | null\n if (!input || typeof input !== 'object') return []\n const path = typeof input.file_path === 'string' ? input.file_path : null\n if (!path) return []\n if (typeof input.content === 'string') return [{ path, lines: input.content.split('\\n') }]\n if (typeof input.new_string === 'string') return [{ path, lines: input.new_string.split('\\n') }]\n if (Array.isArray(input.edits)) {\n const lines: string[] = []\n for (const e of input.edits) {\n const ns = (e as Record<string, unknown>)?.new_string\n if (typeof ns === 'string') lines.push(...ns.split('\\n'))\n }\n return lines.length ? [{ path, lines }] : []\n }\n return []\n}\n\n/** OpenCode file writes → (path, authored lines). OpenCode uses camelCase fields:\n * - `write` → { filePath, content }\n * - `edit` → { filePath, oldString, newString }\n * - `apply_patch` → { patchText } in the Codex `*** Begin Patch` format (gpt-5 models only)\n */\nfunction opencodeAuthored(t: ToolCall): { path: string; lines: string[] }[] {\n const input = t.input as Record<string, unknown> | null\n if (!input || typeof input !== 'object') return []\n // apply_patch carries a whole multi-file patch string, not a single filePath.\n if (typeof input.patchText === 'string') return parseCodexPatch(input.patchText)\n const path =\n typeof input.filePath === 'string' ? input.filePath : typeof input.file_path === 'string' ? input.file_path : typeof input.path === 'string' ? input.path : null\n if (!path) return []\n if (typeof input.content === 'string') return [{ path, lines: input.content.split('\\n') }] // write\n if (typeof input.newString === 'string') return [{ path, lines: input.newString.split('\\n') }] // edit\n return []\n}\n\n/** Codex apply_patch (raw patch string) → per-file added (`+`) lines. */\nfunction codexAuthored(t: ToolCall): { path: string; lines: string[] }[] {\n return typeof t.input === 'string' ? parseCodexPatch(t.input) : []\n}\n\n/** Parse a Codex/OpenCode `*** Begin Patch` string → per-file added (`+`) lines.\n * Shared by Codex `apply_patch` (raw string input) and OpenCode `apply_patch`\n * (`{ patchText }`); both use identical `*** Add/Update/Delete File:` + `*** Move to:`\n * headers. */\nexport function parseCodexPatch(patch: string): { path: string; lines: string[] }[] {\n if (!patch) return []\n const byFile = new Map<string, string[]>()\n let cur: string | null = null\n for (const line of patch.split('\\n')) {\n const h = line.match(/^\\*\\*\\* (?:Add|Update|Delete) File: (.+)$/)\n if (h) {\n cur = h[1]!.trim()\n if (!byFile.has(cur)) byFile.set(cur, [])\n continue\n }\n const mv = line.match(/^\\*\\*\\* Move to: (.+)$/)\n if (mv) {\n cur = mv[1]!.trim()\n if (!byFile.has(cur)) byFile.set(cur, [])\n continue\n }\n if (cur && line.startsWith('+') && !line.startsWith('+++')) byFile.get(cur)!.push(line.slice(1))\n }\n return [...byFile].map(([path, lines]) => ({ path, lines }))\n}\n\n/** Absolute/relative authored path → repo-relative, or null if it escapes the repo. */\nfunction repoRel(rawPath: string, cwd: string, repoRoot: string): string | null {\n const abs = isAbsolute(rawPath) ? rawPath : resolve(cwd, rawPath)\n if (abs !== repoRoot && !abs.startsWith(repoRoot + '/')) return null\n return abs.slice(repoRoot.length).replace(/^\\/+/, '')\n}\n\n// ---- normalization (line-level; see docs/plans/pr-linkage.md) --------------\n\n/** Normalize → drop blank/whitespace-only/pure-bracket lines, preserving order. */\nfunction meaningful(lines: string[]): string[] {\n const out: string[] = []\n for (const l of lines) {\n const n = l.replace(/\\s+/g, ' ').trim()\n if (n === '' || /^[{}()[\\];,<>]+$/.test(n) || n === '=>' || n === '});') continue\n out.push(n)\n }\n return out\n}\n\n// ---- git helpers -----------------------------------------------------------\n\nasync function gitToplevel(sh: Sh, cwd: string): Promise<string | null> {\n const res = await sh('git', ['-C', cwd, 'rev-parse', '--show-toplevel'])\n return ok(res) ? res!.stdout.trim() : null\n}\n\nasync function gitOwnerRepo(sh: Sh, cwd: string): Promise<string | null> {\n const res = await sh('git', ['-C', cwd, 'remote', 'get-url', 'origin'])\n if (!ok(res)) return null\n const m = res!.stdout.trim().match(/[:/]([^/:]+)\\/([^/]+?)(?:\\.git)?$/)\n return m ? `${m[1]}/${m[2]}` : null\n}\n\nfunction ok(res: ShResult | null): boolean {\n return !!res && res.code === 0 && res.stdout.trim().length > 0\n}\n\nregisterProcessor(prContentMatch)\n","import { registerProcessor } from '../core/registry'\nimport { blockMembership, blockSpine, deterministicBlocks } from '../core/blocks'\nimport { enrichPrArtifact, parsePrRefs, prArtifactBase } from './github-pr'\nimport type { Block } from '../core/blocks'\nimport type { FeatureRef, Processor, ProcessorContext, ProcessorResult } from '../core/processor'\nimport type { Session } from '../core/model'\nimport { isSyntheticUser, stripReminders } from '../core/turns'\nimport { costOfUsage } from '../pricing/pricing'\nimport type { JsonSchema } from '../llm/types'\nimport type {\n AnnotationInput,\n ArtifactInput,\n BlockAnnotationInput,\n BlockArtifactInput,\n FeatureRevisionInput,\n OutcomeInput,\n SessionArtifactInput,\n} from '../store/types'\n\nconst USE_CASES = ['plan', 'implement', 'debug', 'research', 'review', 'docs', 'other']\nconst COMPLEXITY = ['trivial', 'routine', 'substantial', 'open-ended']\nconst AUTONOMY = ['autonomous', 'guided', 'minimal']\nconst SUCCESS = ['success', 'partial', 'failure', 'unknown']\n\n/**\n * LLM enrichment in one batched structured call per session: use-case (multi),\n * complexity, autonomy, intent summary, key decisions, judged success — plus\n * feature linkage.\n *\n * `decisions` captures the consequential choices that shaped the session — a\n * technical approach taken, a tradeoff accepted, a scope cut, a library/tool\n * picked, an alternative explicitly rejected. It is deliberately separate from\n * `intent_summary` (which now states only the goal) so a reader can see WHAT was\n * decided, not just what was attempted. Each entry is one self-contained line\n * with the rationale inline; an empty list means nothing consequential was decided.\n *\n * `autonomy` measures how much the agent ran on its own, classified from how much\n * the user STEERED it rather than from the model's general impression (which\n * biased everything to \"guided\" — AL-33). It is a spectrum: \"autonomous\" (agent\n * ran end-to-end on minimal input beyond approvals) → \"guided\" (human gave\n * substantive direction at key points, agent executed) → \"minimal\" (heavy\n * involvement, frequent corrections/redirections). The digest surfaces a \"steering\n * signal\" — the count of substantive follow-up turns after the opener — but the\n * prompt asks the model to weigh their NATURE, since a follow-up may be genuine\n * direction (\"use Postgres instead\") or mere workflow progression (\"commit and\n * open a PR\", \"mark it done\"), and only the latter-vs-former call decides\n * autonomous-vs-guided. Two upstream fixes make this work: `userTurns` drops\n * Claude-injected pseudo-user turns (slash-command echoes, local-command\n * caveats/stdout) so the opener is the first REAL prompt, and the count excludes\n * bare approvals. Both matter because the user spine is truncated for long\n * sessions, so the model cannot reliably reconstruct the count itself.\n *\n * The feature half is hierarchy-aware. The model sees the existing features as an\n * indented tree and is asked to (a) attach the session to the MOST SPECIFIC\n * feature that fits, (b) when nothing fits, propose a new (source='derived')\n * feature slotted under the right parent, and (c) reparent a feature it is\n * advancing via `feature_revisions`. Auto-rename is NOT offered: a bad rename\n * retroactively mislabels every session under a feature, so titles are fixed at\n * creation. User-authored features are shown but locked (never reparented).\n * Because the runner reads the tree fresh per session, edits compound across a run.\n */\nexport const enrichSession: Processor = {\n name: 'enrich-session',\n version: 17,\n kind: 'enrichment',\n needs: { llm: true },\n requires: ['segment-blocks'],\n facets: [\n // use_case is BLOCK-grain: a session's spend time-slices across its blocks'\n // use-cases (P3). Session-level filter/detail roll up via block_annotations.\n { key: 'use_case', label: 'Work type', type: 'enum', source: 'block', roles: ['chart', 'filter', 'detail'] },\n { key: 'complexity', label: 'Complexity', type: 'enum', source: 'annotation', roles: ['chart', 'filter', 'detail'] },\n { key: 'autonomy', label: 'Autonomy', type: 'enum', source: 'annotation', roles: ['chart', 'filter', 'detail'] },\n // NOTE: `success` is deliberately NOT a facet. The LLM judgment is surfaced to\n // users only as the `session_success` outcome; the raw 4-way grade stays as\n // a plain annotation (below) for traceability, not exposed in any UI.\n ],\n async run(ctx: ProcessorContext): Promise<ProcessorResult> {\n const { llm, session } = ctx\n if (!llm) return {}\n\n const blocks = deterministicBlocks(session)\n const refs = parsePrRefs(session)\n const deterministicPrs = new Set(refs.filter((r) => r.kind === 'create' || r.kind === 'merge').map((r) => r.id))\n const userLinkedPrs = ctx.userLinkedArtifacts.filter((a) => a.kind === 'pr' && !deterministicPrs.has(a.artifactId))\n const userLinkedFeatures = ctx.userLinkedArtifacts.filter((a) => a.kind === 'feature')\n const prContext: PrPromptContext = { userLinkedPrs, prBlockAttributions: ctx.prBlockAttributions }\n const { system, user } = buildPrompt(session, ctx.existingFeatures, blocks, ctx.rejectedFeatureTitles, prContext, userLinkedFeatures)\n const { data: parsed, usage } = await llm.completeStructured({\n system,\n user,\n schema: outputSchema(blocks.length, userLinkedPrs.length > 0),\n toolName: TOOL_NAME,\n // Headroom for the full structured output on large sessions\n maxTokens: 4096,\n })\n const selfCost = { tokens: usage, usd: costOfUsage(llm.provider, llm.model, usage) }\n\n if (Object.keys(parsed).length === 0) {\n ctx.log.warn(`enrich-session: empty LLM output for ${session.id}`)\n return { selfCost } // record the spend; don't re-charge on every run\n }\n\n // Session-level classification (P4: success/autonomy/complexity stay per-session).\n // use_case is NOT here — it's block-grain (see block labels below).\n const annotations: AnnotationInput[] = [\n { key: 'complexity', value: oneOf(parsed.complexity, COMPLEXITY) },\n { key: 'autonomy', value: oneOf(parsed.autonomy, AUTONOMY) },\n // Trace-only: the raw 4-way grade is kept for inspection but is not a facet\n // (see facets[] above). The user-facing signal is the outcome below.\n { key: 'success', value: oneOf(parsed.success, SUCCESS) },\n { key: 'intent_summary', value: str(parsed.intent_summary) },\n { key: 'decisions', value: decisionList(parsed.decisions) },\n ]\n // Intent-based display title (read at display time, falling back to the native title)\n const title = sessionTitle(parsed.title)\n if (title) annotations.push({ key: 'title', value: title })\n\n // The LLM-judged \"did this session accomplish its task(s)\" signal lives in the\n // outcomes list (alongside git-derived pr_merged etc.), not as a facet.\n const outcomes: OutcomeInput[] = []\n if (oneOf(parsed.success, SUCCESS) === 'success') {\n outcomes.push({ type: 'session_success', artifactId: null, ts: session.endedAt })\n }\n\n const repo = session.project.repo ?? null\n const existing = new Map(ctx.existingFeatures.map((f) => [f.id, f]))\n const isLocked = (id: string) => existing.get(id)?.source === 'user'\n // Repo isolation guard: an existing feature is a safe auto-link target only if\n // it is unscoped (global, e.g. a cross-repo epic) or already touches this repo.\n // A feature owned by other repos is never auto-linked or auto-edited from here.\n const inRepo = (f: FeatureRef) => !f.repos?.length || (repo != null && f.repos.includes(repo))\n // A new derived feature may nest under user-owned (epic) parents or any\n // parent this repo may use; otherwise it stays top-level.\n const canParent = (id: string) => {\n const p = existing.get(id)\n return !!p && (p.source === 'user' || inRepo(p))\n }\n\n // Feature linkage is BLOCK-FIRST: the model attaches features to block ranges\n // (feature_runs); the session's feature set is the UNION of its blocks'\n // features (exactly like the use_case rollup). The `features` palette only\n // NAMES the candidates that feature_runs reference by index — a palette entry\n // never tied to a block is dropped, so session-level and block-level features\n // can never diverge. We never link across repos.\n // Features the user explicitly linked to THIS session (session_artifacts.source='user').\n // Used two ways: an explicit link overrides the inRepo auto-link guard below (so a\n // cross-repo linked feature resolves to itself instead of spawning a duplicate), and it\n // guards the session-link re-emit further down from clobbering the user's row.\n const userLinkedIds = new Set(ctx.userLinkedArtifacts.map((a) => a.artifactId))\n const features = Array.isArray(parsed.features) ? parsed.features : []\n const palette: Array<{ id: string; create?: ArtifactInput }> = []\n for (const f of features) {\n const matched = str(f?.matched_feature_id)\n const mf = matched ? existing.get(matched) : undefined\n if (mf && (inRepo(mf) || userLinkedIds.has(mf.id))) { palette.push({ id: mf.id }); continue } // same-repo/global, or an explicit user link (overrides repo isolation)\n // No usable match → propose a repo-scoped derived feature (created only if used).\n const title = featureTitle(f?.new_title ?? f?.title) ?? (mf ? featureTitle(mf.title) : null)\n if (!title) { palette.push({ id: '' }); continue }\n const id = derivedFeatureId(repo, title)\n const parent = str(f?.parent_id)\n const parentArtifactId = parent && parent !== id && canParent(parent) ? parent : undefined\n palette.push({ id, create: { id, kind: 'feature', title, source: 'derived', repo: repo ?? undefined, parentArtifactId } })\n }\n\n // Block labels: use_case per block (use_case_runs) + block→feature links\n // (feature_runs: sparse, non-overlapping, palette-resolved). `linked`\n // accumulates the UNION of features any block advanced.\n const blockAnnotations: BlockAnnotationInput[] = []\n const blockArtifacts: BlockArtifactInput[] = []\n const linked = new Set<string>()\n // Per-block use-case, computed once and reused by the reviewed-PR gate below.\n const useCases = blocks.length > 0 ? expandUseCaseRuns(parsed.use_case_runs, blocks.length) : []\n if (blocks.length > 0) {\n useCases.forEach((uc, idx) => blockAnnotations.push({ blockIdx: idx, key: 'use_case', value: uc }))\n const assigned = new Set<number>()\n for (const run of parseRuns(parsed.feature_runs, 'feature')) {\n const id = palette[run.idx]?.id || ''\n if (!id) continue\n linked.add(id)\n for (let i = Math.max(0, run.from); i <= Math.min(blocks.length - 1, run.to); i++) {\n if (assigned.has(i)) continue\n assigned.add(i)\n blockArtifacts.push({ blockIdx: i, artifactId: id, role: 'contributed', source: 'derived', confidence: 0.5 })\n }\n }\n }\n\n // PR block attribution from LLM: assign user-linked PRs to blocks not already\n // claimed by deterministic PR attribution (outcomes-git).\n if (userLinkedPrs.length > 0 && blocks.length > 0) {\n const prTaken = new Set(ctx.prBlockAttributions.map((a) => a.blockIdx))\n const prAssigned = new Set<number>()\n for (const run of parseRuns(parsed.pr_runs, 'pr')) {\n const pr = userLinkedPrs[run.idx]\n if (!pr) continue\n for (let i = Math.max(0, run.from); i <= Math.min(blocks.length - 1, run.to); i++) {\n if (prTaken.has(i) || prAssigned.has(i)) continue\n prAssigned.add(i)\n blockArtifacts.push({ blockIdx: i, artifactId: pr.artifactId, role: 'contributed', source: 'derived', confidence: 0.6 })\n }\n }\n }\n\n // Session → feature links + new-feature creation, DERIVED from the block union:\n // only features with block coverage are linked/created, so the Summary and the\n // transcript's View-by features always agree.\n const artifacts: ArtifactInput[] = []\n const sessionArtifacts: SessionArtifactInput[] = []\n const emitted = new Set<string>()\n // userLinkedIds (hoisted above): an artifact the user explicitly linked to THIS session\n // must not be re-emitted — INSERT OR REPLACE would clobber its source/producer/confidence.\n // Covers user-created features (artifacts.source='user') AND derived features the user\n // linked via the dashboard (artifacts.source='derived' but sa.source='user').\n for (const slot of palette) {\n if (!slot.id || !linked.has(slot.id) || emitted.has(slot.id)) continue\n emitted.add(slot.id)\n if (slot.create) artifacts.push(slot.create)\n if (!userLinkedIds.has(slot.id)) {\n sessionArtifacts.push({ artifactId: slot.id, role: 'contributed', source: 'derived', confidence: 0.6 })\n }\n }\n\n // Taxonomy upkeep: REPARENT only, and only for a feature this session advanced\n // (in `linked`). Auto-rename stays disabled; locked/user features untouched.\n const featureRevisions: FeatureRevisionInput[] = []\n const revs = Array.isArray(parsed.feature_revisions) ? parsed.feature_revisions : []\n for (const r of revs) {\n const id = str(r?.feature_id)\n if (!id || isLocked(id) || !linked.has(id)) continue\n const rawParent = str(r?.new_parent_id)\n let parentId: string | null | undefined\n if (rawParent === 'root') parentId = null\n else if (rawParent && rawParent !== id && canParent(rawParent)) parentId = rawParent\n if (parentId === undefined) continue\n featureRevisions.push({ id, parentId })\n }\n\n // Reviewed-PR linkage (Layer 2, derived): a PR READ inside a `review`-labeled block\n // links this session to it as `reviewed`. Unlike Layer 1's explicit `gh pr review`\n // there's no submission event, so we FORWARD-FILL: a read is where a PR ENTERS the\n // review; the review blocks after it (analysis with no new read) belong to it until\n // the next PR is read, and leading review blocks backfill to the first read. Bounded\n // to review blocks (the LLM labeled them), so it never bleeds into production. Soft\n // signal → confidence 0.6. Excludes self-created PRs and any PR Layer 1 already owns.\n const selfCreated = deterministicPrs\n const explicitReviewed = new Set(refs.filter((r) => r.kind === 'review').map((r) => r.id))\n const toolBlock = blocks.length > 0 ? blockMembership(session, blocks).tool : []\n const seqToBlock: number[] = []\n for (const b of blocks) for (let s = b.startSeq; s <= b.endSeq; s++) seqToBlock[s] = b.idx\n\n // Each review block's target PR = the one READ earliest in it (1-1 within reviews).\n // A tool read orders by toolIndex; a human-pasted link (toolIndex -1) opens the block\n // via its user turn, so it precedes any tool read (key -1). Tightened gate: the read\n // must land IN a review block. A PR conflicted out of every block it was read in (an\n // earlier read won that block) is dropped entirely below — no link, no cost.\n const target = new Map<number, { pr: string; key: number }>()\n for (const ref of refs) {\n if (ref.kind !== 'read' || selfCreated.has(ref.id) || explicitReviewed.has(ref.id)) continue\n const bi = ref.toolIndex >= 0 ? toolBlock[ref.toolIndex] : ref.atSeq != null ? seqToBlock[ref.atSeq] : undefined\n if (bi == null || useCases[bi] !== 'review') continue\n const key = ref.toolIndex >= 0 ? ref.toolIndex : -1\n const prev = target.get(bi)\n if (!prev || key < prev.key) target.set(bi, { pr: ref.id, key })\n }\n\n // Forward-fill the review blocks: carry the last read's PR across analysis blocks;\n // leading review blocks (before the first read) backfill to the first target.\n const reviewBlockPr = new Map<number, string>()\n let curReview: string | undefined\n const pendingReview: number[] = []\n for (let bi = 0; bi < blocks.length; bi++) {\n if (useCases[bi] !== 'review') continue\n const hit = target.get(bi)?.pr\n if (hit) {\n curReview = hit\n for (const p of pendingReview) reviewBlockPr.set(p, hit)\n pendingReview.length = 0\n reviewBlockPr.set(bi, hit)\n } else if (curReview) {\n reviewBlockPr.set(bi, curReview)\n } else {\n pendingReview.push(bi) // review block before any read — resolves at the first read\n }\n }\n\n // Only PRs that actually won a review block get linked — a PR conflicted out of every\n // block it was read in earns no link at all (its cost stays with the block's winner).\n for (const id of new Set(reviewBlockPr.values())) {\n const ref = refs.find((r) => r.id === id)! // any ref carrying this identity\n artifacts.push(await enrichPrArtifact(ctx.sh, prArtifactBase(ref), session.project.cwd))\n sessionArtifacts.push({ artifactId: id, role: 'reviewed', source: 'derived', confidence: 0.6 })\n outcomes.push({ type: 'pr_reviewed', artifactId: id, ts: session.endedAt })\n }\n for (const [bi, id] of reviewBlockPr) {\n blockArtifacts.push({ blockIdx: bi, artifactId: id, role: 'reviewed', source: 'derived', confidence: 0.6 })\n }\n\n return {\n annotations,\n outcomes,\n artifacts,\n sessionArtifacts,\n featureRevisions,\n blockAnnotations,\n blockArtifacts,\n selfCost,\n }\n },\n}\n\nregisterProcessor(enrichSession)\n\n// ---- prompt -----------------------------------------------------------------\n\nimport type { UserLinkedArtifact, PrBlockAttribution } from '../core/processor'\n\ninterface PrPromptContext {\n userLinkedPrs: UserLinkedArtifact[]\n prBlockAttributions: PrBlockAttribution[]\n}\n\nfunction buildPrompt(session: Session, features: FeatureRef[], blocks: Block[], rejectedTitles?: string[], prContext?: PrPromptContext, userLinkedFeatures?: UserLinkedArtifact[]): { system: string; user: string } {\n const system =\n 'You analyze a single AI coding session, classify it, and maintain a hierarchical product-feature map. ' +\n `Report your analysis by calling the ${TOOL_NAME} tool; its parameters define every field and its allowed values.`\n\n const featureTree = features.length\n ? renderFeatureTree(features)\n : '(empty — no features yet)'\n const repoLabel = session.project.repo ?? '(no repo)'\n\n const user = [\n `Summary of an AI coding session. This session's repo is \"${repoLabel}\".`,\n '',\n digest(session),\n '',\n `Blocks — the session split into ${blocks.length} contiguous slice(s); boundaries are FIXED (label every one):`,\n blockSpine(session, blocks),\n '',\n 'The full feature map across all repos (indentation = parent → child; \"(locked)\" = user-owned;',\n '{...} = the repos that feature spans, \"{any repo}\" = unscoped/global):',\n featureTree,\n '',\n ...(rejectedTitles?.length\n ? [\n 'The user has REJECTED the following feature associations for this session. Do NOT propose them or semantically similar features:',\n ...rejectedTitles.map((t) => `- \"${t}\"`),\n '',\n ]\n : []),\n ...(userLinkedFeatures?.length\n ? [\n 'The user has EXPLICITLY linked the following features to this session. You MUST match them in your \"features\" list (via matched_feature_id) and assign at least one block to each in feature_runs:',\n ...userLinkedFeatures.map((f) => `- [${f.artifactId}] \"${f.title ?? f.artifactId}\"`),\n '',\n ]\n : []),\n ...buildPrPromptSection(prContext, blocks.length),\n `Fill the ${TOOL_NAME} tool. Field shapes and allowed values are defined by the tool schema; the guidance below`,\n 'explains how to choose each value.',\n 'How to write the title — name the OVERALL intent of the WHOLE session in a short Title-Case noun phrase',\n '(about 3–7 words). It should read like a PR title or a changelog heading: what the session set out to',\n 'accomplish, taken as a whole — not its first message, not a play-by-play, not a full sentence.',\n '- Capture the dominant goal. If the session did several things, name the primary thread, not a list.',\n '- Be specific and concrete (mention the actual feature/area), e.g. \"Error-Category Fingerprinting & Drill-Down\",',\n ' \"Codex Token Double-Count Fix\", \"Session Outcome-Rate Dashboard\" — not vague (\"Code Changes\", \"Bug Fixes\").',\n '- No trailing period, no quotes, no comma-spliced run-ons, and do not start with \"The user\"/\"We\".',\n 'How to classify complexity — judge the DIFFICULTY of what the session set out to do, not how long it ran or',\n 'whether it succeeded. The axis runs from mechanical effort up to specification clarity (how well-defined the',\n 'goal and the shape of a correct answer were at the outset).',\n '- \"trivial\": a tiny, fully-specified, mechanical change — a typo, rename, one-line fix, or config tweak. Obvious how to do it.',\n '- \"routine\": standard, well-understood work with a clear goal and a known approach — a typical feature, an ordinary bugfix, or following an established pattern. The path is clear even if it takes a while.',\n '- \"substantial\": large or intricate but still specifiable — spans many files or systems, or needs real design or coordination, yet the goal and the shape of a correct answer are known up front.',\n '- \"open-ended\": no clear specification — the goal is ambiguous and even the shape of a correct answer is unclear at the outset, so the work requires exploration or diagnosis just to figure out what \"done\" means. A clear comprehension goal (\"understand how X works\") is NOT open-ended, however much reading it takes — open-ended means you could not tell what a correct answer looks like at the outset, not merely that the work was read-heavy.',\n 'How to classify autonomy — it measures how much the AGENT ran on its own. Look at the user turns AFTER the',\n 'opening request and judge how much they STEERED the work. Steering = a correction, a redirection, a design',\n 'choice, or a new requirement. NOT steering: the opening request itself, bare approvals (\"yes\", \"continue\",',\n '\"looks good\"), and routine progression that only advances the workflow (\"commit and open a PR\", \"mark it',\n 'done\", \"now do the next one\") — those mean the user is letting the agent run, not directing it.',\n '- \"autonomous\": the agent executed end-to-end on minimal input beyond approvals — no or very few genuinely',\n ' steering turns; the agent decided HOW to do the work. A session kicked off with one request and then only',\n ' nudged forward (\"commit\", \"open a PR\", \"mark it done\") is autonomous, even if it ran long.',\n '- \"guided\": the user gave substantive direction at KEY POINTS — choosing an approach, correcting course, or',\n ' adding requirements — and the agent handled execution in between.',\n '- \"minimal\": minimal agent autonomy — frequent corrections or redirections, a tight back-and-forth where the',\n ' user steered most steps.',\n 'Do NOT default to \"guided\": when the user mostly approved and let the agent run, choose \"autonomous\".',\n 'What a key decision IS: a consequential choice that shaped the work and that a teammate reviewing this',\n 'session later would want to know — a technical approach chosen, a tradeoff accepted, a scope cut, a',\n 'library/tool/data-model picked, or an alternative explicitly rejected. Prefer the user\\'s steering and the',\n 'reasoning behind a turn over mechanical steps.',\n 'Rules for decisions:',\n '- Each entry is ONE self-contained sentence; fold the rationale in with \"because\"/\"to\"/\"over\" (\"Chose SQLite over Postgres to stay local-first\").',\n '- Capture only what was actually settled in THIS session — not restated background, open questions, or routine edits (renames, formatting, obvious fixups).',\n '- Aim for the few decisions that genuinely mattered (typically 0–6). Use [] when the work carried no real decision (a chore, a trivial fix, pure research).',\n '- State each decision factually; do not begin entries with \"The user\" or \"We\".',\n 'What a feature IS: one coherent product capability, named as a SHORT noun phrase (2–5 words, Title Case).',\n ' Good names: \"Cost-per-PR metric\", \"Session outcome rate\", \"Dashboard KPI tiles\", \"Feature hierarchy extraction\".',\n 'A feature name is NOT a summary of the session. Never string capabilities together with commas or \"and\".',\n ' Bad (a session summary crammed into one feature): \"analyze command with cost-per-PR metric, session outcome rate tracking, and adoption positioning\"',\n ' Good (name the ONE dominant feature): \"Session outcome rate\".',\n 'Rules for the feature map:',\n '- Map the session to AS FEW features as possible — ideally EXACTLY ONE: the single feature it primarily advanced. List that primary feature first.',\n '- Add a second feature (or, rarely, a third) ONLY when the session SUBSTANTIALLY advanced a genuinely separate capability. When unsure, pick just the one dominant feature. Incidental edits, fixups, or supporting changes do NOT each become a feature.',\n `- Repo isolation: only match (matched_feature_id) a feature tagged for this repo (\"${repoLabel}\") or tagged \"{any repo}\". To advance a feature that belongs only to OTHER repos, create a new feature instead — do not match it.`,\n '- Attach to the MOST SPECIFIC eligible feature via matched_feature_id; prefer matching an existing feature over creating a new one.',\n '- Do not split one capability into several features, and do not merge several capabilities into one run-on title.',\n '- Only set new_title when no eligible feature is specific enough. Keep it a short noun phrase (never a sentence or a list); place it under the best parent via parent_id (a same-repo, \"{any repo}\", or \"(locked)\" epic), leaving parent_id empty only for a genuinely top-level capability.',\n '- feature_revisions is ONLY for moving a feature you advanced (one you matched or created above) under a better parent. You CANNOT rename features. Never touch features you did not advance, other repos\\' features, or \"(locked)\" features.',\n '- parent_id / new_parent_id must reference an id that already exists in the map above.',\n '- If the work is not feature-level (e.g. a chore, refactor, or pure research), use empty \"features\" and \"feature_revisions\" arrays.',\n 'Rules for use_case_runs (the use-case of each block, in time order):',\n `- Cover EVERY block index 0..${Math.max(0, blocks.length - 1)} with contiguous, non-overlapping runs (a partition). Merge adjacent blocks of the same use-case into one run; expect FEW runs (work changes use-case only a few times).`,\n '- Pick the single dominant use_case per run.',\n '- Bias toward bundling with the previous block: a block that does no substantive work of its own and only carries the prior work to a checkpoint (committing, pushing, opening/merging a PR, marking a task done, a confirming test run) inherits the PREVIOUS run\\'s use_case — do not start a new run, and never an \"other\" run, for mechanical follow-through.',\n 'What each use_case means (pick the best fit; when two could apply, choose the one the block\\'s actions center on):',\n '- plan: deciding the SHAPE of the solution (architecture, interfaces, data models, tradeoffs, laying out an approach, breaking work into steps) — before or with little code written yet.',\n '- implement: writing or changing code/config to build or modify functionality — the default for hands-on building.',\n '- debug: diagnosing and fixing a specific failure — reproducing, tracing, and correcting a bug, test failure, or error.',\n '- research: searching external or internal data sources for gathering information — reading docs, analyzing data, web search, or learning something.',\n '- review: evaluating existing code or a change for quality/correctness (PR or code review, critique, security/architecture audit), not authoring the feature.',\n '- docs: writing or updating human-facing prose — docs, READMEs, guides, marketing/product copy.',\n '- other: work that fits none of the above — chores, dependency bumps, environment/CI setup.',\n 'Rules for feature_runs (which blocks advanced which feature):',\n '- SPARSE: emit a run ONLY for blocks that substantially advanced a feature; chores/research/fixups belong to no feature — leave them out.',\n '- \"feature\" is the 0-based index into the \"features\" array above. Runs must not overlap. Most sessions need 0–2 feature_runs.',\n '- The session is credited a feature ONLY through feature_runs, so give every feature you list in \"features\" at least one feature_run; a feature with no run is dropped.',\n ...(prContext?.userLinkedPrs.length\n ? [\n 'Rules for pr_runs (which blocks contributed to which user-linked PR):',\n '- SPARSE: assign only blocks whose work clearly fed into the PR. Runs must not overlap with each other.',\n '- \"pr\" is the 0-based index into the user-linked PRs list above.',\n '- Do NOT assign blocks that are already attributed to other PRs (listed as \"FIXED\" above).',\n '- Every user-linked PR should get at least one block run if possible — the user explicitly linked it.',\n ]\n : []),\n ].join('\\n')\n\n return { system, user }\n}\n\nconst TOOL_NAME = 'record_analysis'\n\n/**\n * The structured-output contract: the single source of truth for the SHAPE of\n * enrichment output (field names, types, allowed values), exposed as the forced\n * tool's input schema; the prompt carries only the classification guidance. The\n * parsers below still normalize defensively, so this is a strong hint, not a\n * guarantee (providers vary in how strictly they honor it).\n */\nfunction outputSchema(nBlocks: number, hasUserLinkedPrs: boolean): JsonSchema {\n const lastBlock = Math.max(0, nBlocks - 1)\n const required = ['title', 'complexity', 'autonomy', 'intent_summary', 'decisions', 'success', 'features', 'feature_revisions', 'use_case_runs', 'feature_runs']\n const properties: Record<string, unknown> = {\n title: { type: 'string', description: 'Short Title-Case phrase naming the OVERALL intent of the session (see guidance).' },\n complexity: { type: 'string', enum: COMPLEXITY, description: 'Difficulty of what the session set out to do.' },\n autonomy: { type: 'string', enum: AUTONOMY, description: 'How much the agent ran on its own.' },\n intent_summary: { type: 'string', description: 'One sentence: what the user set out to accomplish (the goal, not the decisions).' },\n decisions: { type: 'array', items: { type: 'string' }, description: 'The KEY decisions made during the session, newest insight last; [] if none.' },\n success: { type: 'string', enum: SUCCESS, description: 'Whether the session accomplished its task(s).' },\n features: {\n type: 'array',\n description: 'Features this session advanced — ideally exactly one; list the primary feature first.',\n items: {\n type: 'object',\n properties: {\n matched_feature_id: { type: 'string', description: 'Id of the most specific existing feature this session advanced, or empty.' },\n new_title: { type: 'string', description: 'Title for a NEW feature when none fit, else empty.' },\n parent_id: { type: 'string', description: 'Existing feature id to nest the new feature under, or empty for top-level.' },\n },\n },\n },\n feature_revisions: {\n type: 'array',\n description: 'Reparent a feature THIS session advanced under a better parent; [] otherwise.',\n items: {\n type: 'object',\n properties: {\n feature_id: { type: 'string', description: 'A feature this session advances, from the features above.' },\n new_parent_id: { type: 'string', description: 'Existing feature id to reparent under, \"root\" for top-level, or empty to keep.' },\n },\n },\n },\n use_case_runs: {\n type: 'array',\n description: `Partition blocks 0..${lastBlock} into contiguous, non-overlapping runs, each with one use_case.`,\n items: {\n type: 'object',\n properties: {\n from: { type: 'integer', description: 'First block index in the run.' },\n to: { type: 'integer', description: 'Last block index in the run.' },\n use_case: { type: 'string', enum: USE_CASES },\n },\n },\n },\n feature_runs: {\n type: 'array',\n description: 'Sparse: which block ranges advanced which feature.',\n items: {\n type: 'object',\n properties: {\n from: { type: 'integer', description: 'First block index in the run.' },\n to: { type: 'integer', description: 'Last block index in the run.' },\n feature: { type: 'integer', description: '0-based index into the features array.' },\n },\n },\n },\n }\n if (hasUserLinkedPrs) {\n required.push('pr_runs')\n properties.pr_runs = {\n type: 'array',\n description: 'Sparse: which block ranges contributed to which user-linked PR.',\n items: {\n type: 'object',\n properties: {\n from: { type: 'integer', description: 'First block index in the run.' },\n to: { type: 'integer', description: 'Last block index in the run.' },\n pr: { type: 'integer', description: '0-based index into the user-linked PRs list.' },\n },\n },\n }\n }\n return { type: 'object', additionalProperties: false, required, properties }\n}\n\nfunction buildPrPromptSection(prContext: PrPromptContext | undefined, nBlocks: number): string[] {\n if (!prContext?.userLinkedPrs.length) return []\n const lines: string[] = []\n // Show blocks already claimed by deterministic PR attribution\n const takenBlocks = new Map<number, string>()\n for (const a of prContext.prBlockAttributions) {\n takenBlocks.set(a.blockIdx, a.title ?? a.artifactId)\n }\n if (takenBlocks.size > 0) {\n lines.push('Blocks already attributed to PRs (FIXED — do NOT assign these to another PR in pr_runs):')\n const grouped = new Map<string, number[]>()\n for (const [idx, label] of takenBlocks) {\n const arr = grouped.get(label) ?? []\n arr.push(idx)\n grouped.set(label, arr)\n }\n for (const [label, idxs] of grouped) {\n lines.push(`- Blocks ${idxs.sort((a, b) => a - b).join(', ')} → \"${label}\"`)\n }\n lines.push('')\n }\n // List unattributed blocks available for assignment\n const available: number[] = []\n for (let i = 0; i < nBlocks; i++) {\n if (!takenBlocks.has(i)) available.push(i)\n }\n lines.push(`User-linked PRs needing block attribution (assign from available blocks: ${available.join(', ')}):`)\n prContext.userLinkedPrs.forEach((pr, idx) => {\n const label = pr.title ?? pr.ident ?? pr.artifactId\n lines.push(`- (index ${idx}) ${label}`)\n })\n lines.push('')\n return lines\n}\n\n/** Render the feature list as an indented tree (parent → child) for the prompt. */\nfunction renderFeatureTree(features: FeatureRef[]): string {\n const ids = new Set(features.map((f) => f.id))\n const childrenOf = new Map<string, FeatureRef[]>()\n for (const f of features) {\n // Treat a feature whose parent is missing from the set as a root, so nothing is dropped.\n const key = f.parentId && ids.has(f.parentId) ? f.parentId : ''\n const arr = childrenOf.get(key) ?? []\n arr.push(f)\n childrenOf.set(key, arr)\n }\n const lines: string[] = []\n const seen = new Set<string>()\n const walk = (f: FeatureRef, depth: number) => {\n if (seen.has(f.id)) return // guard against parent cycles\n seen.add(f.id)\n const lock = f.source === 'user' ? ' (locked)' : ''\n const repos = f.repos && f.repos.length ? f.repos.join(', ') : 'any repo'\n lines.push(`${' '.repeat(depth)}- [${f.id}] ${f.title}${lock} {${repos}}`)\n for (const c of childrenOf.get(f.id) ?? []) walk(c, depth + 1)\n }\n for (const root of childrenOf.get('') ?? []) walk(root, 0)\n for (const f of features) if (!seen.has(f.id)) walk(f, 0) // any left by a cycle\n return lines.join('\\n')\n}\n\nfunction digest(s: Session): string {\n const turns = userTurns(s)\n const followups = followupTurns(turns)\n const files = unique(s.toolCalls.filter((t) => t.action === 'file_write').flatMap((t) => t.target.paths ?? [])).slice(0, 40)\n const cmds = s.toolCalls\n .filter((t) => t.action === 'shell' && t.target.command)\n .map((t) => (t.target.command as string).replace(/\\s+/g, ' ').slice(0, 120))\n .slice(0, 20)\n const tail = assistantTail(s).slice(-1200)\n\n // Activity profile: breadth of work, biased toward signals the complexity\n // rubric needs — reads/searches/web/subagents reveal exploration & diagnosis\n // that file-writes alone hide (a heavy investigation can write almost nothing).\n const nAction = (a: string) => s.toolCalls.filter((t) => t.action === a).length\n const distinctPaths = (a: string) =>\n unique(s.toolCalls.filter((t) => t.action === a).flatMap((t) => t.target.paths ?? [])).length\n const activity = [\n `${distinctPaths('file_write')} written`,\n `${distinctPaths('file_read')} read`,\n `${nAction('search')} search`,\n `${nAction('web')} web`,\n `${nAction('task_spawn')} subagent`,\n `${nAction('mcp_call')} mcp`,\n `${nAction('shell')} shell`,\n ].join(', ')\n\n return [\n `Models: ${s.models.join(', ') || 'unknown'}`,\n `User turns: ${turns.length} | Tool calls: ${s.toolCalls.length}`,\n `Activity profile (distinct files for read/write, else call counts — gauges scope & exploration): ${activity}`,\n `Steering signal: ${followups.length} of ${turns.length} user turn(s) were follow-ups after the opening ` +\n `request (bare approvals like \"yes\"/\"continue\" already excluded). Judge how many GENUINELY steered the ` +\n `work — a correction, redirection, design choice, or new requirement — versus routine \"do the next step\" ` +\n `progression (\"commit and open a PR\", \"mark it done\"), which is the user letting the agent run. Only ` +\n `genuine steering pushes toward guided/minimal.`,\n '',\n 'User messages (the human side, in order — read these for intent, steering, and reactions):',\n userSpine(turns),\n '',\n `Files written (${files.length}):`,\n files.map((f) => `- ${f}`).join('\\n') || '(none)',\n '',\n 'Shell commands (sample):',\n cmds.map((c) => `- ${c}`).join('\\n') || '(none)',\n '',\n 'Final assistant message (tail):',\n tail || '(none)',\n ].join('\\n')\n}\n\n/**\n * All main-thread human turns, in order. Excludes sidechain (subagent) turns,\n * strips injected reminders, and drops Claude-injected pseudo-user turns —\n * slash-command echoes, local-command caveats/stdout, interrupts, tool\n * rejections. Those are machinery, not the human steering the agent; counting\n * them skewed the opener (the first REAL prompt) and the autonomy signal (AL-33).\n */\nfunction userTurns(s: Session): string[] {\n const out: string[] = []\n for (const ev of s.events) {\n if (ev.kind !== 'user' || ev.isSidechain) continue\n const t = stripReminders(ev.text)\n if (t && !isSyntheticUser(t)) out.push(t)\n }\n return out\n}\n\n/**\n * Substantive follow-up turns: the user turns AFTER the opening request, minus\n * bare approvals/continuations (\"yes\", \"continue\"). This is a CEILING on\n * steering, not steering itself — a follow-up may be genuine direction\n * (\"use Postgres instead\") or mere workflow progression (\"commit and open a PR\",\n * \"mark it done\"), and only the model can tell those apart from the text. The\n * count feeds the autonomy classification (autonomous → guided → minimal as\n * genuine steering rises; see AL-33). Deliberately conservative: only whole-turn\n * known approvals are dropped, so nothing substantive is hidden from the model.\n */\nfunction followupTurns(turns: string[]): string[] {\n return turns.slice(1).filter((t) => !isApproval(t))\n}\n\n/** A short, content-free affirmation/continuation (\"yes\", \"ok continue\") that lets the agent proceed rather than redirecting it. */\nfunction isApproval(text: string): boolean {\n const t = text.toLowerCase().replace(/[^a-z0-9\\s]/g, ' ').replace(/\\s+/g, ' ').trim()\n if (!t) return true\n if (t.split(' ').length > 6) return false // too long to be a bare approval\n return APPROVAL_RE.test(t)\n}\n\n// Whole-turn approval/continuation phrases (matched against punctuation-stripped,\n// lowercased text). Kept conservative — when unsure, a turn counts as steering.\nconst APPROVAL_RE =\n /^(y|yes|yep|yup|yeah|ya|ok|okay|k|kk|sure|fine|cool|great|perfect|nice|good|awesome|excellent|thanks|thank you|thanks a lot|thank you so much|ty|thx|continue|please continue|proceed|go|go ahead|go for it|go on|do it|do that|keep going|carry on|next|lgtm|looks good|looks great|that works|sounds good|ship it|approved|correct|right|exactly|agreed|got it|makes sense|yes please|ok thanks|perfect thanks|great thanks|yes continue|ok continue|ok go ahead|sure go ahead)$/\n\n/** Keep every turn within budget; for very long sessions keep the opening + most recent and elide the middle. */\nfunction userSpine(turns: string[], perMsg = 800, totalBudget = 6000): string {\n if (turns.length === 0) return '(none)'\n const labeled = turns.map((t, i) => `[${i + 1}] ${t.length > perMsg ? t.slice(0, perMsg) + ' …' : t}`)\n if (labeled.length <= 8 || labeled.join('\\n').length <= totalBudget) return labeled.join('\\n')\n const head = labeled.slice(0, 3)\n const tail = labeled.slice(-5)\n return [...head, `… (${labeled.length - head.length - tail.length} middle message(s) omitted) …`, ...tail].join('\\n')\n}\n\nfunction assistantTail(s: Session): string {\n const parts: string[] = []\n for (const ev of s.events) {\n if (ev.kind !== 'assistant') continue\n for (const b of ev.blocks) if (b.type === 'text' && b.text.trim()) parts.push(b.text)\n }\n return parts.join('\\n')\n}\n\n// ---- parsing / sanitizing ---------------------------------------------------\n\nfunction str(v: unknown): string {\n return typeof v === 'string' ? v.trim() : ''\n}\n\nfunction strArray(v: unknown): string[] {\n return Array.isArray(v) ? v.map(str).filter(Boolean) : []\n}\n\n/**\n * Sanitize the model's `decisions` into a small set of clean, distinct one-liners.\n * Drops blanks and exact duplicates, trims trailing punctuation noise, and caps\n * the count so a verbose model can't bury the genuinely key decisions.\n */\nfunction decisionList(v: unknown): string[] {\n const seen = new Set<string>()\n const out: string[] = []\n for (const raw of strArray(v)) {\n const d = raw.replace(/\\s+/g, ' ').trim()\n if (!d) continue\n const key = d.toLowerCase()\n if (seen.has(key)) continue\n seen.add(key)\n out.push(d)\n if (out.length >= 8) break\n }\n return out\n}\n\nfunction oneOf(v: unknown, allowed: string[]): string {\n const s = str(v).toLowerCase()\n return allowed.includes(s) ? s : 'unknown'\n}\n\nfunction intOf(v: unknown): number | null {\n return typeof v === 'number' && Number.isInteger(v) ? v : null\n}\n\n/**\n * Expand the model's use_case_runs into one use_case per block, tiling [0,n).\n * Out-of-range / invalid runs are clamped or dropped. Unlabeled blocks fall to\n * 'unclassified'; a gap bracketed by the SAME label on both sides inherits it\n * (use-case is sticky). A miss degrades quality, never cost (see plan).\n */\nfunction expandUseCaseRuns(v: unknown, n: number): string[] {\n const out = new Array<string>(n).fill('unclassified')\n const filled = new Array<boolean>(n).fill(false)\n if (Array.isArray(v)) {\n for (const r of v) {\n const uc = oneOf(r?.use_case, USE_CASES)\n const from = intOf(r?.from)\n const to = intOf(r?.to)\n if (uc === 'unknown' || from == null || to == null) continue\n for (let i = Math.max(0, Math.min(from, to)); i <= Math.min(n - 1, Math.max(from, to)); i++) {\n out[i] = uc\n filled[i] = true\n }\n }\n }\n for (let i = 0; i < n; i++) {\n if (filled[i]) continue\n let p = i - 1\n while (p >= 0 && !filled[p]) p--\n let q = i + 1\n while (q < n && !filled[q]) q++\n if (p >= 0 && q < n && out[p] === out[q]) out[i] = out[p]!\n }\n return out\n}\n\n/** Normalize feature_runs into clamped {from,to,feature} entries (caller resolves the palette index). */\nfunction parseRuns(v: unknown, key: string): Array<{ from: number; to: number; idx: number }> {\n if (!Array.isArray(v)) return []\n const out: Array<{ from: number; to: number; idx: number }> = []\n for (const r of v) {\n const from = intOf(r?.from)\n const to = intOf(r?.to)\n const idx = intOf(r?.[key])\n if (from == null || to == null || idx == null) continue\n out.push({ from: Math.min(from, to), to: Math.max(from, to), idx })\n }\n return out\n}\n\nfunction unique<T>(arr: T[]): T[] {\n return [...new Set(arr)]\n}\n\n/**\n * Accept a model-proposed feature name only if it reads like a feature (a short\n * noun phrase), rejecting the run-on \"session summary\" strings a weaker model\n * sometimes emits — long, comma-spliced, or many-claused. Returning null drops\n * the proposal (no junk feature enters the taxonomy) rather than storing garbage.\n */\nfunction featureTitle(raw: unknown): string | null {\n const t = str(raw)\n if (!t) return null\n if (t.length > 72 || t.split(/\\s+/).length > 9 || t.includes(',')) return null\n return t\n}\n\n/**\n * Clean the model's session `title` for storage: collapse whitespace and peel any\n * wrapping quotes / trailing period the model tends to add. We store the FULL title\n * (display truncation is the UI's job, so search/hover keep the whole string);\n */\nexport function sessionTitle(raw: unknown): string | undefined {\n let t = str(raw).replace(/\\s+/g, ' ').trim()\n for (let prev = ''; prev !== t; ) {\n prev = t\n t = t.replace(/^[\"'`]+|[\"'`.]+$/g, '').trim() // \"Title\". → Title (any order, repeated)\n }\n return t || undefined\n}\n\n/** Repo-qualified id for a derived feature, so identical titles in different repos don't collide. */\nfunction derivedFeatureId(repo: string | null, title: string): string {\n return `feature:derived:${repo ? slug(repo) : 'norepo'}:${slug(title)}`\n}\n\nfunction slug(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60) || 'untitled'\n}\n","import { homedir } from 'node:os'\nimport { join, resolve } from 'node:path'\nimport { PROVIDERS } from './llm/providers'\n\n/** Resolved runtime configuration for a single invocation. */\nexport interface TuneloopConfig {\n /** Directory holding the SQLite store and other local state. */\n dataDir: string\n dbPath: string\n /** LLM provider for enrichment (BYO key), or null when not configured. */\n llm: { provider: string; model: string; apiKey: string; baseURL?: string } | null\n}\n\n/** Non-secret LLM knobs settable via CLI flags; they override env. The API key is env-only. */\nexport interface LlmOverrides {\n provider?: string\n model?: string\n baseURL?: string\n}\n\nfunction resolveLlm(o?: LlmOverrides): TuneloopConfig['llm'] {\n const provider = (o?.provider ?? process.env.TUNELOOP_LLM_PROVIDER)?.toLowerCase()\n if (!provider) return null\n const preset = PROVIDERS[provider]\n\n // Key is env-only: TUNELOOP_LLM_API_KEY wins, else the preset's conventional env.\n // Keyless local endpoints (Ollama) get a placeholder the SDK accepts.\n const apiKey =\n process.env.TUNELOOP_LLM_API_KEY ?? (preset ? process.env[preset.keyEnv] : undefined) ?? (preset?.requiresKey === false ? 'local' : '')\n // Needs-a-key but none → stay static-only (the analyze hint covers it). resolveLlm\n // never throws: unknown provider / missing base-URL / empty model are recoverable\n // misconfig that createLlmClient validates inside analyze's graceful try/catch, so a\n // typo can't abort the run — nor the read-only `serve`, which builds no client.\n if (preset && preset.requiresKey !== false && !apiKey) return null\n\n const model = o?.model ?? process.env.TUNELOOP_LLM_MODEL ?? preset?.defaultModel ?? ''\n const baseURL = o?.baseURL ?? process.env.TUNELOOP_LLM_BASE_URL ?? preset?.baseURL\n return { provider, model, apiKey, baseURL }\n}\n\nexport function loadConfig(opts?: { dataDir?: string; db?: string; llm?: LlmOverrides }): TuneloopConfig {\n const dataDir = resolve(opts?.dataDir ?? process.env.TUNELOOP_DATA_DIR ?? join(homedir(), '.tuneloop'))\n const dbPath = resolve(opts?.db ?? join(dataDir, 'tuneloop.sqlite'))\n return { dataDir, dbPath, llm: resolveLlm(opts?.llm) }\n}\n","/**\n * Provider presets — the single source of truth for which LLM endpoints tuneloop\n * can drive. Almost every provider speaks the OpenAI Chat Completions wire\n * format, so they all share ONE `openai-compatible` shape that differs only by\n * `baseURL`; a preset is sugar that bakes in the URL, the default key env, and a\n * sensible default model. Anthropic is the lone native exception.\n *\n * To add a provider, add a row here — nothing else changes.\n */\nexport type ProviderShape = 'anthropic' | 'openai' | 'openai-compatible'\n\nexport interface ProviderPreset {\n shape: ProviderShape\n /** Required for openai-compatible; omitted for the native SDK defaults. */\n baseURL?: string\n /** Default env var the API key is read from when TUNELOOP_LLM_API_KEY is unset. */\n keyEnv: string\n defaultModel: string\n /** false for local endpoints that need no key (Ollama). */\n requiresKey?: boolean\n}\n\nexport const PROVIDERS: Record<string, ProviderPreset> = {\n anthropic: { shape: 'anthropic', keyEnv: 'ANTHROPIC_API_KEY', defaultModel: 'claude-haiku-4-5' },\n openai: { shape: 'openai', keyEnv: 'OPENAI_API_KEY', defaultModel: 'gpt-5.4-mini' },\n\n openrouter: { shape: 'openai-compatible', baseURL: 'https://openrouter.ai/api/v1', keyEnv: 'OPENROUTER_API_KEY', defaultModel: 'openai/gpt-5-mini' },\n groq: { shape: 'openai-compatible', baseURL: 'https://api.groq.com/openai/v1', keyEnv: 'GROQ_API_KEY', defaultModel: 'llama-3.3-70b-versatile' },\n deepseek: { shape: 'openai-compatible', baseURL: 'https://api.deepseek.com', keyEnv: 'DEEPSEEK_API_KEY', defaultModel: 'deepseek-chat' },\n gemini: { shape: 'openai-compatible', baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/', keyEnv: 'GEMINI_API_KEY', defaultModel: 'gemini-2.5-flash' },\n together: { shape: 'openai-compatible', baseURL: 'https://api.together.xyz/v1', keyEnv: 'TOGETHER_API_KEY', defaultModel: 'deepseek-ai/DeepSeek-V3' },\n fireworks: { shape: 'openai-compatible', baseURL: 'https://api.fireworks.ai/inference/v1', keyEnv: 'FIREWORKS_API_KEY', defaultModel: 'accounts/fireworks/models/deepseek-v3' },\n xai: { shape: 'openai-compatible', baseURL: 'https://api.x.ai/v1', keyEnv: 'XAI_API_KEY', defaultModel: 'grok-4' },\n // Local: no key. Enrichment uses forced tool calls, so pick a tool-capable\n ollama: { shape: 'openai-compatible', baseURL: 'http://localhost:11434/v1', keyEnv: 'OLLAMA_API_KEY', defaultModel: 'qwen2.5', requiresKey: false },\n\n // Escape hatch for anything unlisted; requires TUNELOOP_LLM_BASE_URL.\n 'openai-compatible': { shape: 'openai-compatible', keyEnv: 'TUNELOOP_LLM_API_KEY', defaultModel: '' },\n}\n\nexport const PROVIDER_NAMES = Object.keys(PROVIDERS)\n","import type { Session } from './model'\nimport type { FeatureRef, Processor, ProcessorContext } from './processor'\nimport type { LlmClient } from '../llm/types'\nimport type { Store } from '../store/store'\nimport type { Logger } from '../util/log'\n\n/** Topologically order processors so a processor runs after everything in `requires`. */\nexport function orderProcessors(procs: Processor[]): Processor[] {\n const byName = new Map(procs.map((p) => [p.name, p]))\n const out: Processor[] = []\n const done = new Set<string>()\n const onStack = new Set<string>()\n\n const visit = (p: Processor) => {\n if (done.has(p.name)) return\n if (onStack.has(p.name)) return // dependency cycle — break it\n onStack.add(p.name)\n for (const dep of p.requires ?? []) {\n const d = byName.get(dep)\n if (d) visit(d)\n }\n onStack.delete(p.name)\n done.add(p.name)\n out.push(p)\n }\n for (const p of procs) visit(p)\n return out\n}\n\nexport interface RunOptions {\n session: Session\n processors: Processor[]\n store: Store\n log: Logger\n llmEnabled: boolean\n llmModel: string | null\n llm: LlmClient | null\n sh: ProcessorContext['sh']\n}\n\nexport interface RunResult {\n costUsd: number\n}\n\n/** Run every applicable processor for one session, honoring deps + the cache. */\nexport async function runProcessors(opts: RunOptions): Promise<RunResult> {\n const { session, store, log, llmEnabled, llmModel, llm, sh } = opts\n // Read the feature hierarchy fresh for every session, not once per run, so a\n // session sees features that earlier sessions in this run created, renamed, or\n // reparented — letting the extractor grow one coherent tree instead of dupes.\n // The whole (cross-repo) hierarchy is sent; repo isolation is enforced on\n // linkage inside the processor, not by hiding other repos' features.\n const existingFeatures: FeatureRef[] = store.listFeatures()\n const rejectedFeatureTitles = store.rejectedFeatureTitles(session.id)\n const allUserLinked = store.userLinkedArtifactsAll(session.id)\n const userLinkedArtifacts = allUserLinked\n .filter((a) => a.kind === 'feature' || !a.hasNonEnrichBlocks)\n .map(({ hasNonEnrichBlocks: _, ...rest }) => rest)\n const prBlockAttributions = store.prBlockAttributions(session.id)\n const ctx: ProcessorContext = { session, log, llmEnabled, llm, existingFeatures, rejectedFeatureTitles, userLinkedArtifacts, prBlockAttributions, sh }\n // The cache key is the session's content hash alone. Link/unlink no longer\n // perturbs the hash — those actions invalidate the affected processor_runs\n // rows directly (Store.invalidateSessionProcessors), so a re-linked artifact\n // can never collide with a stale cached run the way a reversible hash suffix could.\n const inputHash = session.raw.contentHash\n let costUsd = 0\n\n for (const p of orderProcessors(opts.processors)) {\n if (p.needs?.llm && !llmEnabled) continue\n const model = p.needs?.llm ? llmModel : null\n\n // Re-read block attributions fresh so enrich-session sees blocks that\n // outcomes-git persisted earlier in this same loop. Without this, the\n // first analysis of a session sees an empty FIXED set and the LLM can\n // double-attribute a block to both a deterministic PR and a linked PR.\n ctx.prBlockAttributions = store.prBlockAttributions(session.id)\n\n const prior = store.processorRun(session.id, p.name)\n if (prior && !prior.invalidated && prior.version === p.version && prior.inputHash === inputHash && prior.model === model) {\n log.debug(`cached ${p.name} for ${session.id}`)\n continue\n }\n\n try {\n const result = await p.run(ctx)\n store.persistResult(session.id, p.name, p.version, inputHash, model, result)\n costUsd += result.selfCost?.usd ?? 0\n } catch (err) {\n log.warn(`processor ${p.name} failed on ${session.id}: ${(err as Error).message}`)\n }\n }\n return { costUsd }\n}\n","import Anthropic from '@anthropic-ai/sdk'\nimport { parseJsonObject } from './json'\nimport type { ClientOpts, LlmClient, LlmResult, StructuredRequest } from './types'\n\n/** Anthropic-backed client. Data goes only to Anthropic, with the user's own key. */\nexport function createAnthropicClient(apiKey: string, model: string, opts?: ClientOpts): LlmClient {\n const client = new Anthropic({ apiKey })\n return {\n provider: opts?.provider ?? 'anthropic',\n model,\n async completeStructured(req: StructuredRequest): Promise<LlmResult> {\n const { system, user, schema, toolName, maxTokens = 1024 } = req\n const resp = await client.messages.create({\n model,\n max_tokens: maxTokens,\n system,\n messages: [{ role: 'user', content: user }],\n tools: [{ name: toolName, description: 'Record the structured analysis.', input_schema: schema as Anthropic.Tool.InputSchema }],\n tool_choice: { type: 'tool', name: toolName },\n })\n // The forced tool's input IS the structured result; salvage any text if absent.\n for (const b of resp.content) {\n if (b.type === 'tool_use' && b.name === toolName) return { data: b.input as Record<string, unknown>, usage: usageOf(resp.usage) }\n }\n return { data: parseJsonObject(textOf(resp.content)) ?? {}, usage: usageOf(resp.usage) }\n },\n }\n}\n\nfunction textOf(content: Anthropic.ContentBlock[]): string {\n return content.map((b) => (b.type === 'text' ? b.text : '')).join('')\n}\n\nfunction usageOf(u: Anthropic.Usage) {\n return {\n input: u.input_tokens ?? 0,\n output: u.output_tokens ?? 0,\n cacheCreate: u.cache_creation_input_tokens ?? 0,\n cacheRead: u.cache_read_input_tokens ?? 0,\n }\n}\n","/**\n * Parse a JSON object out of a model's text response — tolerant of the markdown\n * fences and stray prose weaker models wrap around their JSON. Used to salvage\n * output when a model returns its result as text instead of a tool call. Returns\n * null if nothing object-shaped can be recovered.\n */\nexport function parseJsonObject(text: string): Record<string, unknown> | null {\n const tryParse = (s: string): Record<string, unknown> | null => {\n try {\n const v = JSON.parse(s)\n return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : null\n } catch {\n return null\n }\n }\n const direct = tryParse(text.trim())\n if (direct) return direct\n const match = text.match(/\\{[\\s\\S]*\\}/)\n return match ? tryParse(match[0]) : null\n}\n","import OpenAI from 'openai'\nimport { parseJsonObject } from './json'\nimport type { ClientOpts, LlmClient, LlmResult, StructuredRequest } from './types'\n\n/**\n * OpenAI / OpenAI-compatible client over the Chat Completions API — the universal\n * endpoint every compatible provider implements (OpenRouter, Groq, DeepSeek,\n * Together, Fireworks, xAI, Gemini-compat, Ollama). `baseURL` selects the\n * endpoint; `provider` is the name self-cost pricing keys on.\n *\n * Structured output is one forced function call whose arguments ARE the result,\n * so the model must support tool calling. `strict` is NOT set: many compatible\n * providers do function calling but not strict json-schema, and enrich-session\n * normalizes defensively anyway. Token cap uses `max_completion_tokens` (native\n * gpt-5/o-series require it; compatible endpoints accept it — verified on\n * Ollama/Groq/Gemini).\n *\n * Model choice matters: pick one that doesn't burn the token budget on hidden\n * reasoning before emitting the tool call. The default `gpt-5.4-mini` doesn't\n * reason on this Chat-Completions+tools path (reasoning there is gated to the\n * Responses API), so it never starves. An older reasoning-by-default model like\n * `gpt-5-mini` can exhaust the budget reasoning and return empty output on large\n * sessions — prefer the default.\n */\nexport function createOpenAiClient(apiKey: string, model: string, opts?: ClientOpts): LlmClient {\n const client = new OpenAI({ apiKey, baseURL: opts?.baseURL })\n return {\n provider: opts?.provider ?? 'openai',\n model,\n async completeStructured(req: StructuredRequest): Promise<LlmResult> {\n const { system, user, schema, toolName, maxTokens = 1024 } = req\n const resp = await client.chat.completions.create({\n model,\n max_completion_tokens: maxTokens,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content: user },\n ],\n tools: [{ type: 'function', function: { name: toolName, description: 'Record the structured analysis.', parameters: schema } }],\n tool_choice: { type: 'function', function: { name: toolName } },\n })\n const msg = resp.choices[0]?.message\n const call = msg?.tool_calls?.find((c) => c.type === 'function' && c.function.name === toolName)\n if (call?.type === 'function') return { data: parseJsonObject(call.function.arguments) ?? {}, usage: usageOf(resp.usage) }\n // No tool call came back; salvage any plain-text JSON.\n return { data: parseJsonObject(msg?.content ?? '') ?? {}, usage: usageOf(resp.usage) }\n },\n }\n}\n\nfunction usageOf(u: OpenAI.Completions.CompletionUsage | undefined) {\n const cached = u?.prompt_tokens_details?.cached_tokens ?? 0\n return {\n input: Math.max(0, (u?.prompt_tokens ?? 0) - cached),\n output: u?.completion_tokens ?? 0,\n cacheCreate: 0,\n cacheRead: cached,\n }\n}\n","import type { TuneloopConfig } from '../config'\nimport { createAnthropicClient } from './anthropic'\nimport { createOpenAiClient } from './openai'\nimport { PROVIDERS } from './providers'\nimport type { ClientOpts, LlmClient } from './types'\n\nexport type { LlmClient, LlmResult, StructuredRequest, JsonSchema } from './types'\nexport { PROVIDERS, PROVIDER_NAMES } from './providers'\n\n/** Build an LLM client from config, or null if enrichment isn't configured. */\nexport function createLlmClient(llm: TuneloopConfig['llm']): LlmClient | null {\n if (!llm) return null\n // Sole validator for recoverable enrichment misconfig: these throws land in\n // analyze's try/catch and degrade to static-only, instead of aborting the command.\n const preset = PROVIDERS[llm.provider]\n if (!preset) throw new Error(`unknown LLM provider: ${llm.provider} (supported: ${Object.keys(PROVIDERS).join(', ')})`)\n if (!llm.model) throw new Error(`provider \"${llm.provider}\" needs a model — set TUNELOOP_LLM_MODEL or --llm-model`)\n if (preset.shape === 'openai-compatible' && !llm.baseURL) {\n throw new Error(`provider \"${llm.provider}\" needs a base URL — set TUNELOOP_LLM_BASE_URL or --llm-base-url`)\n }\n const opts: ClientOpts = { provider: llm.provider, baseURL: llm.baseURL }\n switch (preset.shape) {\n case 'anthropic':\n return createAnthropicClient(llm.apiKey, llm.model, opts)\n case 'openai':\n case 'openai-compatible':\n return createOpenAiClient(llm.apiKey, llm.model, opts)\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport { gunzipSync, gzipSync } from 'node:zlib'\nimport type { CanonicalAction, Session, ToolCall } from '../core/model'\nimport type { ProcessorResult, RefreshResult } from '../core/processor'\nimport type { DB } from './db'\nimport { parseApplyPatch } from './apply-patch'\nimport { deterministicBlocks } from '../core/blocks'\nimport { classifyError, ERROR_CATEGORIES } from '../core/error-category'\nimport { facetGroupCompatible, grainOf } from '../core/facets'\nimport type { FacetSpec, FacetType, Grain } from '../core/facets'\nimport { aliasFor } from '../core/measures'\nimport type { MeasureSpec } from '../core/measures'\nimport { isSyntheticUser } from '../core/turns'\nimport type { ArtifactInput, FeatureRevisionInput, ProcessorRunRow, SessionArtifactRole, UsageFactInput } from './types'\n\nexport interface Dist {\n value: string\n count: number\n}\n\n/** One failed tool call in the \"Errors by category\" drill-down (see errorOccurrences). */\nexport interface ErrorOccurrence {\n sessionId: string\n title: string | null\n idx: number\n name: string\n action: string\n command: string | null\n targetPath: string | null\n message: string | null\n ts: string | null\n startedAt: string | null\n}\n\nexport interface Summary {\n sessions: number\n costUsd: number\n tokens: number\n firstAt: string | null\n lastAt: string | null\n models: Array<{ model: string; count: number }>\n outcomes: Array<{ type: string; count: number }>\n topTools: Array<{ name: string; calls: number; errors: number }>\n costPerMergedPr: { count: number; costPerUnit: number | null }\n /** Spend on enrichment (the \"cost of running the analysis itself\"). */\n analysisCostUsd: number\n /** Whether LLM enrichment has run (any processor recorded an LLM model). */\n enrichmentRan: boolean\n /** ISO timestamp of the most recent `analyze` run (null if never recorded). */\n lastAnalyzedAt: string | null\n /** Source directories scanned, each with its own last-analyzed time (empty until an analyze runs on this schema). */\n analyzedRoots: Array<{ source: string | null; path: string; lastAnalyzedAt: string | null }>\n /** Enrichment dimension distributions, empty when enrichment hasn't run. */\n useCases: Dist[]\n complexity: Dist[]\n autonomy: Dist[]\n features: { total: number; derived: number; linked: number }\n}\n\n/** One computed insight for the Highlights digest. The client maps `kind` to the\n * display sentence + its drill-in; the payload carries the data. */\nexport interface Highlight {\n kind: string\n [field: string]: unknown\n}\n\nexport class Store {\n constructor(private db: DB) {}\n\n /** Read a value from the key-value `meta` table (undefined when absent). */\n getMeta(key: string): string | undefined {\n const row = this.db.prepare('SELECT value FROM meta WHERE key = ?').get(key) as { value: string } | undefined\n return row?.value\n }\n\n /** Upsert a value into the key-value `meta` table. */\n setMeta(key: string, value: string): void {\n this.db.prepare('INSERT OR REPLACE INTO meta(key, value) VALUES (?, ?)').run(key, value)\n }\n\n /**\n * Stamp each source directory scanned this run with the run timestamp. Upsert,\n * so roots a scoped re-run didn't touch keep their prior stamp — the table then\n * answers \"when was THIS directory last analyzed\" per directory.\n */\n recordAnalyzedRoots(roots: Array<{ source: string; path: string }>, at: string): void {\n const stmt = this.db.prepare(\n `INSERT INTO analyzed_roots (source, path, last_analyzed_at) VALUES (?, ?, ?)\n ON CONFLICT(source, path) DO UPDATE SET last_analyzed_at = excluded.last_analyzed_at`,\n )\n const tx = this.db.transaction((rows: Array<{ source: string; path: string }>) => {\n for (const r of rows) stmt.run(r.source, r.path, at)\n })\n tx(roots)\n }\n\n /**\n * Content hash + parse version for a session, if already ingested. Both feed\n * the re-ingest decision: content_hash catches changed transcripts, parse\n * version catches a smarter parser (new fields extracted from the same bytes).\n */\n storedMeta(id: string): { hash: string; parseVersion: number } | undefined {\n const row = this.db\n .prepare('SELECT content_hash AS hash, parse_version AS parseVersion FROM sessions WHERE id = ?')\n .get(id) as { hash: string; parseVersion: number } | undefined\n return row\n }\n\n /** Set a session's resolved repo. Used to backfill repo without a full re-ingest. */\n setSessionRepo(id: string, repo: string) {\n this.db.prepare('UPDATE sessions SET repo = ? WHERE id = ?').run(repo, id)\n }\n\n ingestSession(\n session: Session,\n costUsd: number,\n facts: UsageFactInput[],\n priceTableVersion: string,\n parseVersion: number,\n ) {\n const nTurns = session.events.filter((e) => e.kind === 'user').length\n const tx = this.db.transaction(() => {\n // Upsert (NOT INSERT OR REPLACE): replacing the row would fire ON DELETE\n // CASCADE and wipe processor-owned children (annotations, outcomes,\n // session_artifacts) that a cache-hit processor then won't recreate. Update\n // in place so re-ingest only refreshes the session's own columns.\n this.db\n .prepare(\n `INSERT INTO sessions (\n id, session_id, source, provider, title, repo, branch, cwd,\n started_at, ended_at, n_turns, n_tool_calls, models,\n tok_input, tok_output, tok_cache_create, tok_cache_read,\n cost_usd, price_table_version, content_hash, parse_version, analyzed_at\n ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)\n ON CONFLICT(id) DO UPDATE SET\n session_id=excluded.session_id, source=excluded.source, provider=excluded.provider,\n title=excluded.title, repo=excluded.repo, branch=excluded.branch, cwd=excluded.cwd,\n started_at=excluded.started_at, ended_at=excluded.ended_at, n_turns=excluded.n_turns,\n n_tool_calls=excluded.n_tool_calls, models=excluded.models,\n tok_input=excluded.tok_input, tok_output=excluded.tok_output,\n tok_cache_create=excluded.tok_cache_create, tok_cache_read=excluded.tok_cache_read,\n cost_usd=excluded.cost_usd, price_table_version=excluded.price_table_version,\n content_hash=excluded.content_hash, parse_version=excluded.parse_version,\n analyzed_at=excluded.analyzed_at`,\n )\n .run(\n session.id,\n session.sessionId,\n session.source,\n session.provider,\n session.title ?? null,\n session.project.repo ?? null,\n session.project.branch ?? null,\n session.project.cwd ?? null,\n session.startedAt ?? null,\n session.endedAt ?? null,\n nTurns,\n session.toolCalls.length,\n JSON.stringify(session.models),\n session.tokens.input,\n session.tokens.output,\n session.tokens.cacheCreate,\n session.tokens.cacheRead,\n costUsd,\n priceTableVersion,\n session.raw.contentHash,\n parseVersion,\n new Date().toISOString(),\n )\n\n this.db\n .prepare('INSERT OR REPLACE INTO session_blobs (id, gz) VALUES (?, ?)')\n .run(session.id, gzipSync(Buffer.from(JSON.stringify(session))))\n\n this.db.prepare('DELETE FROM tool_calls WHERE session_id = ?').run(session.id)\n const insTool = this.db.prepare(\n `INSERT INTO tool_calls\n (session_id, idx, name, action, ok, is_error, error_category, error_message, target_path, command, is_sidechain, ts, duration_ms)\n VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)`,\n )\n session.toolCalls.forEach((t, idx) => {\n // For failed calls, fingerprint a cross-harness category + keep a one-line\n // error snippet (both NULL when ok), computed at ingest. Clip before classify.\n const errText = t.result.ok ? null : resultText(t.result.raw)\n const category = errText == null ? null : classifyError(t.action, errText.slice(0, 8000))\n const message = errText == null ? null : errText.replace(/\\s+/g, ' ').trim().slice(0, 200) || null\n insTool.run(\n session.id,\n idx,\n t.name,\n t.action,\n t.result.ok ? 1 : 0,\n t.result.isError ? 1 : 0,\n category,\n message,\n t.target.paths?.[0] ?? null,\n t.target.command ?? null,\n t.isSidechain ? 1 : 0,\n t.ts ?? null,\n t.durationMs ?? null,\n )\n })\n\n this.db.prepare('DELETE FROM usage_facts WHERE session_id = ?').run(session.id)\n const insUsage = this.db.prepare(\n `INSERT INTO usage_facts\n (session_id, idx, model, is_sidechain, ts,\n tok_input, tok_output, tok_cache_create, tok_cache_read, cost_usd)\n VALUES (?,?,?,?,?,?,?,?,?,?)`,\n )\n for (const f of facts) {\n insUsage.run(\n session.id,\n f.idx,\n f.model,\n f.isSidechain ? 1 : 0,\n f.ts ?? null,\n f.tokens.input,\n f.tokens.output,\n f.tokens.cacheCreate,\n f.tokens.cacheRead,\n f.usd,\n )\n }\n })\n tx()\n }\n\n /**\n * Token/cost rolled up by model from `usage_facts` — the honest cost-by-model\n * the `sessions.models` array can't give (exploding it double-counts cost).\n */\n usageByModel(): Array<{ model: string; sessions: number; costUsd: number; tokens: number }> {\n return this.db\n .prepare(\n `SELECT model,\n COUNT(DISTINCT session_id) AS sessions,\n SUM(cost_usd) AS costUsd,\n SUM(tok_input + tok_output + tok_cache_create + tok_cache_read) AS tokens\n FROM usage_facts\n GROUP BY model\n ORDER BY costUsd DESC`,\n )\n .all() as Array<{ model: string; sessions: number; costUsd: number; tokens: number }>\n }\n\n /** Prior run record for cache checks. */\n processorRun(sessionId: string, processor: string): ProcessorRunRow | undefined {\n const row = this.db\n .prepare(\n 'SELECT version, input_hash AS inputHash, model, invalidated FROM processor_runs WHERE session_id = ? AND processor = ?',\n )\n .get(sessionId, processor) as (Omit<ProcessorRunRow, 'invalidated'> & { invalidated: number }) | undefined\n return row ? { ...row, invalidated: row.invalidated === 1 } : undefined\n }\n\n unresolvedArtifacts(producer: string): ArtifactInput[] {\n const rows = this.db\n .prepare(\n `SELECT id, kind, repo, ident, external_id AS externalId, source, title, owner,\n complexity, complexity_basis AS complexityBasis, status,\n created_at AS createdAt, completed_at AS completedAt,\n parent_artifact_id AS parentArtifactId\n FROM artifacts\n WHERE producer IN (?, 'dashboard') AND status IS NOT NULL AND status NOT IN ('merged', 'closed', 'shipped')`,\n )\n .all(producer) as ArtifactInput[]\n return rows\n }\n\n persistRefresh(producer: string, result: RefreshResult) {\n const tx = this.db.transaction(() => {\n for (const a of result.artifacts ?? []) {\n this.db\n .prepare(\n `UPDATE artifacts SET status = ?, completed_at = ?,\n complexity = COALESCE(?, complexity),\n complexity_basis = COALESCE(?, complexity_basis)\n WHERE id = ? AND producer IN (?, 'dashboard')`,\n )\n .run(a.status ?? null, a.completedAt ?? null, a.complexity ?? null, a.complexityBasis ?? null, a.id, producer)\n }\n for (const o of result.outcomes ?? []) {\n const sessionId = (\n this.db\n .prepare('SELECT session_id FROM session_artifacts WHERE artifact_id = ? LIMIT 1')\n .get(o.artifactId) as { session_id: string } | undefined\n )?.session_id\n if (!sessionId) continue\n this.db\n .prepare('INSERT INTO outcomes (session_id, type, artifact_id, ts, producer) VALUES (?,?,?,?,?)')\n .run(sessionId, o.type, o.artifactId, o.ts ?? null, producer)\n }\n })\n tx()\n }\n\n /**\n * Persist one processor's output. Replaces this processor's prior rows for the\n * session (provenance via `producer`); never touches other processors' or\n * user-authored rows. Records the run for caching + analysis-cost accounting.\n */\n persistResult(\n sessionId: string,\n processor: string,\n version: number,\n inputHash: string,\n model: string | null,\n result: ProcessorResult,\n ) {\n const tx = this.db.transaction(() => {\n this.db.prepare('DELETE FROM annotations WHERE session_id = ? AND processor = ?').run(sessionId, processor)\n this.db.prepare('DELETE FROM outcomes WHERE session_id = ? AND producer = ?').run(sessionId, processor)\n this.db.prepare('DELETE FROM session_artifacts WHERE session_id = ? AND producer = ?').run(sessionId, processor)\n this.db.prepare('DELETE FROM files_index WHERE session_id = ? AND producer = ?').run(sessionId, processor)\n this.db.prepare('DELETE FROM blocks WHERE session_id = ? AND producer = ?').run(sessionId, processor)\n this.db.prepare('DELETE FROM block_usage WHERE session_id = ? AND producer = ?').run(sessionId, processor)\n this.db.prepare('DELETE FROM block_tool WHERE session_id = ? AND producer = ?').run(sessionId, processor)\n this.db.prepare('DELETE FROM block_annotations WHERE session_id = ? AND processor = ?').run(sessionId, processor)\n this.db.prepare('DELETE FROM block_artifacts WHERE session_id = ? AND producer = ?').run(sessionId, processor)\n\n for (const a of result.annotations ?? []) {\n this.db\n .prepare('INSERT OR REPLACE INTO annotations (session_id, processor, key, value) VALUES (?,?,?,?)')\n .run(sessionId, processor, a.key, JSON.stringify(a.value))\n }\n for (const a of result.artifacts ?? []) {\n if (a.kind === 'feature') {\n // Features are shared, evolving rows: a re-derive that re-proposes the\n // same feature must NOT wipe completion/status/owner a user set on the\n // dashboard, and must never clobber a user-authored feature at all.\n // Upsert title + parent (parent only when a fresh one is supplied) and\n // leave everything else intact; the WHERE guard skips user features.\n this.db\n .prepare(\n `INSERT INTO artifacts\n (id, kind, repo, source, title, created_at, parent_artifact_id, producer)\n VALUES (?, 'feature', ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n title = excluded.title,\n repo = COALESCE(artifacts.repo, excluded.repo),\n parent_artifact_id = COALESCE(excluded.parent_artifact_id, artifacts.parent_artifact_id),\n producer = excluded.producer\n WHERE COALESCE(artifacts.source, '') <> 'user'`,\n )\n .run(a.id, a.repo ?? null, a.source ?? null, a.title ?? null, a.createdAt ?? null, a.parentArtifactId ?? null, processor)\n continue\n }\n // Field-merging upsert (NOT INSERT OR REPLACE): the same PR can be written\n // by several sessions (its creator, then any session that reviewed it), and\n // an offline run may only have a stub (no title/state). COALESCE keeps a\n // value a richer write already stored rather than blanking it. `status` gets\n // a guard so a stub's optimistic 'open' can't overwrite a terminal merged/closed.\n this.db\n .prepare(\n `INSERT INTO artifacts\n (id, kind, repo, ident, external_id, source, title, owner, complexity,\n complexity_basis, status, created_at, completed_at, parent_artifact_id, json, producer)\n VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)\n ON CONFLICT(id) DO UPDATE SET\n repo = COALESCE(excluded.repo, artifacts.repo),\n ident = COALESCE(excluded.ident, artifacts.ident),\n external_id = COALESCE(excluded.external_id, artifacts.external_id),\n source = COALESCE(excluded.source, artifacts.source),\n title = COALESCE(excluded.title, artifacts.title),\n owner = COALESCE(excluded.owner, artifacts.owner),\n complexity = COALESCE(excluded.complexity, artifacts.complexity),\n complexity_basis = COALESCE(excluded.complexity_basis, artifacts.complexity_basis),\n status = CASE\n WHEN artifacts.status IN ('merged', 'closed') AND COALESCE(excluded.status, '') = 'open'\n THEN artifacts.status\n ELSE COALESCE(excluded.status, artifacts.status) END,\n created_at = COALESCE(excluded.created_at, artifacts.created_at),\n completed_at = COALESCE(excluded.completed_at, artifacts.completed_at),\n parent_artifact_id = COALESCE(excluded.parent_artifact_id, artifacts.parent_artifact_id),\n json = COALESCE(excluded.json, artifacts.json),\n producer = excluded.producer`,\n )\n .run(\n a.id, a.kind, a.repo ?? null, a.ident ?? null, a.externalId ?? null, a.source ?? null,\n a.title ?? null, a.owner ?? null, a.complexity ?? null, a.complexityBasis ?? null,\n a.status ?? null, a.createdAt ?? null, a.completedAt ?? null, a.parentArtifactId ?? null,\n a.json === undefined ? null : JSON.stringify(a.json), processor,\n )\n }\n this.applyFeatureRevisions(result.featureRevisions ?? [])\n for (const l of result.links ?? []) {\n this.db\n .prepare(\n 'INSERT OR REPLACE INTO artifact_links (from_id, to_id, relation, source, confidence, producer) VALUES (?,?,?,?,?,?)',\n )\n .run(l.fromId, l.toId, l.relation, l.source, l.confidence ?? null, processor)\n }\n const rejected = new Set(\n (this.db\n .prepare(\"SELECT artifact_id FROM user_link_overrides WHERE session_id = ? AND action = 'reject'\")\n .all(sessionId) as Array<{ artifact_id: string }>)\n .map((r) => r.artifact_id),\n )\n for (const sa of result.sessionArtifacts ?? []) {\n if (sa.source === 'derived' && rejected.has(sa.artifactId)) continue\n this.db\n .prepare(\n 'INSERT OR REPLACE INTO session_artifacts (session_id, artifact_id, role, source, confidence, producer) VALUES (?,?,?,?,?,?)',\n )\n .run(sessionId, sa.artifactId, sa.role, sa.source, sa.confidence ?? null, processor)\n }\n for (const o of result.outcomes ?? []) {\n this.db\n .prepare('INSERT INTO outcomes (session_id, type, artifact_id, ts, producer) VALUES (?,?,?,?,?)')\n .run(sessionId, o.type, o.artifactId ?? null, o.ts ?? null, processor)\n }\n for (const f of result.files ?? []) {\n this.db\n .prepare('INSERT OR REPLACE INTO files_index (repo, path, session_id, producer) VALUES (?,?,?,?)')\n .run(f.repo ?? null, f.path, sessionId, processor)\n }\n for (const b of result.blocks ?? []) {\n this.db\n .prepare(\n 'INSERT OR REPLACE INTO blocks (session_id, idx, start_seq, end_seq, boundary_kind, ts_start, ts_end, producer) VALUES (?,?,?,?,?,?,?,?)',\n )\n .run(sessionId, b.idx, b.startSeq, b.endSeq, b.boundaryKind, b.tsStart ?? null, b.tsEnd ?? null, processor)\n }\n for (const u of result.blockUsage ?? []) {\n this.db\n .prepare('INSERT OR REPLACE INTO block_usage (session_id, usage_idx, block_idx, producer) VALUES (?,?,?,?)')\n .run(sessionId, u.usageIdx, u.blockIdx, processor)\n }\n for (const t of result.blockTool ?? []) {\n this.db\n .prepare('INSERT OR REPLACE INTO block_tool (session_id, tool_idx, block_idx, producer) VALUES (?,?,?,?)')\n .run(sessionId, t.toolIdx, t.blockIdx, processor)\n }\n for (const ba of result.blockAnnotations ?? []) {\n this.db\n .prepare('INSERT OR REPLACE INTO block_annotations (session_id, block_idx, processor, key, value) VALUES (?,?,?,?,?)')\n .run(sessionId, ba.blockIdx, processor, ba.key, JSON.stringify(ba.value))\n }\n // Cross-role block reconciliation — at most one PR-kind row per block. Precedence\n // (highest first): pcm(contributed) > og(reviewed) > enrich(reviewed) > og(contributed).\n // We PREVENT the insert when an equal-or-higher-ranked PR row already holds the block;\n // when this row outranks the holder, we displace it. Feature-contributed rows (rank 0)\n // are a different artifact kind (queried separately) and skip reconciliation. The rank\n // guard makes this ORDER-INDEPENDENT — the same winner regardless of which producer ran\n // (or re-ran) first. Rejected derived links `continue` before touching the block.\n // (Follow-up: a partial UNIQUE index on the PR rows lets INSERT OR REPLACE displace the\n // loser without this explicit DELETE; the guard stays.)\n const heldPr = this.db.prepare(\n `SELECT producer, role FROM block_artifacts WHERE session_id = ? AND block_idx = ?\n AND (producer IN ('outcomes-git', 'pr-content-match') OR (producer = 'enrich-session' AND role = 'reviewed')) LIMIT 1`,\n )\n const displacePr = this.db.prepare(\n `DELETE FROM block_artifacts WHERE session_id = ? AND block_idx = ?\n AND (producer IN ('outcomes-git', 'pr-content-match') OR (producer = 'enrich-session' AND role = 'reviewed'))`,\n )\n const insertBlockArtifact = this.db.prepare(\n 'INSERT OR REPLACE INTO block_artifacts (session_id, block_idx, artifact_id, role, source, confidence, producer) VALUES (?,?,?,?,?,?,?)',\n )\n for (const x of result.blockArtifacts ?? []) {\n if (x.source === 'derived' && rejected.has(x.artifactId)) continue\n const rank = prBlockRank(processor, x.role)\n if (rank > 0) {\n const held = heldPr.get(sessionId, x.blockIdx) as { producer: string; role: string | null } | undefined\n if (held) {\n if (prBlockRank(held.producer, held.role) >= rank) continue // outranked → don't insert\n displacePr.run(sessionId, x.blockIdx) // this row outranks the holder → remove it\n }\n }\n insertBlockArtifact.run(sessionId, x.blockIdx, x.artifactId, x.role, x.source ?? null, x.confidence ?? null, processor)\n }\n\n this.db\n .prepare(\n `INSERT OR REPLACE INTO processor_runs\n (session_id, processor, version, input_hash, model, status, in_tokens, out_tokens, cost_usd, ran_at)\n VALUES (?,?,?,?,?,?,?,?,?,?)`,\n )\n .run(\n sessionId, processor, version, inputHash, model, 'ok',\n result.selfCost?.tokens.input ?? 0, result.selfCost?.tokens.output ?? 0,\n result.selfCost?.usd ?? 0, new Date().toISOString(),\n )\n })\n tx()\n }\n\n /**\n * Apply an enrichment processor's hierarchy edits: REPARENT existing features.\n * (Auto-rename is intentionally unsupported — see FeatureRevisionInput.) Skips\n * user-authored features (locked) and any reparent that would form a cycle or\n * self-parent. Caller runs this inside a transaction.\n */\n private applyFeatureRevisions(revisions: FeatureRevisionInput[]) {\n for (const rev of revisions) {\n if (rev.parentId === undefined) continue\n const row = this.db\n .prepare(\"SELECT source FROM artifacts WHERE id = ? AND kind = 'feature'\")\n .get(rev.id) as { source: string | null } | undefined\n if (!row || row.source === 'user') continue\n const parent = rev.parentId\n if (parent !== rev.id && !this.wouldCreateFeatureCycle(rev.id, parent)) {\n this.db.prepare('UPDATE artifacts SET parent_artifact_id = ? WHERE id = ?').run(parent ?? null, rev.id)\n }\n }\n }\n\n /** True if parenting `id` under `newParentId` would create a cycle (walks ancestors). */\n private wouldCreateFeatureCycle(id: string, newParentId: string | null): boolean {\n let cur = newParentId\n const seen = new Set<string>()\n while (cur) {\n if (cur === id || seen.has(cur)) return true\n seen.add(cur)\n const r = this.db.prepare('SELECT parent_artifact_id AS p FROM artifacts WHERE id = ?').get(cur) as\n | { p: string | null }\n | undefined\n cur = r?.p ?? null\n }\n return false\n }\n\n /**\n * The WHOLE feature hierarchy — what an enrichment processor needs to attach a\n * session to the most specific feature, slot a new feature under the right\n * parent, and refine the tree. The hierarchy is global and human-managed (a\n * single epic may span repos), so the processor sees everything; repo isolation\n * is enforced only on auto-derived *linkage* (see `repos`). `source` flags\n * user-authored features so the processor leaves them locked.\n *\n * `repos` = repos associated anywhere in a feature's subtree (itself + every\n * descendant), unioned from linked sessions and any explicit `repo` column.\n * Empty = unscoped/global. A feature is a safe auto-link target for a session\n * iff its `repos` is empty or already contains the session's repo.\n */\n listFeatures(): Array<{ id: string; title: string; parentId: string | null; source: string | null; repos: string[] }> {\n const repoSets = this.featureRepoSets()\n const rows = this.db\n .prepare(\"SELECT id, COALESCE(title, '') AS title, parent_artifact_id AS parentId, source FROM artifacts WHERE kind = 'feature'\")\n .all() as Array<{ id: string; title: string; parentId: string | null; source: string | null }>\n return rows.map((r) => ({ ...r, repos: repoSets.get(r.id) ?? [] }))\n }\n\n /**\n * Per-feature subtree repo set: the repos associated anywhere in a feature's\n * subtree (itself + every descendant), unioned from each feature's explicit\n * `repo` column and the repos of sessions linked to it. Shared by feature\n * extraction (linkage isolation) and the dashboard (the Features repo column).\n */\n private featureRepoSets(): Map<string, string[]> {\n const rows = this.db\n .prepare(\"SELECT id, parent_artifact_id AS parentId, repo FROM artifacts WHERE kind = 'feature'\")\n .all() as Array<{ id: string; parentId: string | null; repo: string | null }>\n\n const own = new Map<string, Set<string>>()\n const addRepo = (id: string, repo: string | null) => {\n if (!repo) return\n let set = own.get(id)\n if (!set) own.set(id, (set = new Set()))\n set.add(repo)\n }\n for (const r of rows) addRepo(r.id, r.repo)\n const links = this.db\n .prepare(\n `SELECT sa.artifact_id AS id, s.repo AS repo\n FROM session_artifacts sa JOIN sessions s ON s.id = sa.session_id\n JOIN artifacts a ON a.id = sa.artifact_id\n WHERE a.kind = 'feature' AND s.repo IS NOT NULL AND s.repo <> ''`,\n )\n .all() as Array<{ id: string; repo: string }>\n for (const l of links) addRepo(l.id, l.repo)\n\n const children = new Map<string, string[]>()\n for (const r of rows) {\n if (!r.parentId) continue\n const arr = children.get(r.parentId)\n if (arr) arr.push(r.id)\n else children.set(r.parentId, [r.id])\n }\n const memo = new Map<string, Set<string>>()\n const onStack = new Set<string>()\n const subtreeRepos = (id: string): Set<string> => {\n const cached = memo.get(id)\n if (cached) return cached\n const acc = new Set<string>(own.get(id) ?? [])\n if (!onStack.has(id)) {\n onStack.add(id)\n for (const c of children.get(id) ?? []) for (const rp of subtreeRepos(c)) acc.add(rp)\n onStack.delete(id)\n }\n memo.set(id, acc)\n return acc\n }\n const out = new Map<string, string[]>()\n for (const r of rows) out.set(r.id, [...subtreeRepos(r.id)].sort())\n return out\n }\n\n /** Persist facets (intrinsic + processor-declared) so the dashboard discovers them generically. */\n registerFacets(producer: string, specs: FacetSpec[]) {\n const ins = this.db.prepare(\n 'INSERT OR REPLACE INTO facets (key, label, type, source, col, base, multi, roles, producer) VALUES (?,?,?,?,?,?,?,?,?)',\n )\n const tx = this.db.transaction(() => {\n // Sync, don't just upsert: drop any facet this producer registered before\n // but no longer declares, so a removed facet (e.g. a retired enrichment\n // field) leaves the registry instead of lingering as a dead breakdown /\n // filter / distribution option that still groups orphaned annotation rows.\n // Scoped to `producer`, so intrinsic and other processors' facets are safe.\n const keep = specs.map((f) => f.key)\n const notIn = keep.length ? ` AND key NOT IN (${keep.map(() => '?').join(',')})` : ''\n this.db.prepare(`DELETE FROM facets WHERE producer = ?${notIn}`).run(producer, ...keep)\n for (const f of specs) {\n ins.run(\n f.key,\n f.label ?? f.key,\n f.type,\n f.source,\n f.column ?? null,\n f.base ?? null,\n f.multi ? 1 : 0,\n JSON.stringify(f.roles ?? []),\n producer,\n )\n }\n })\n tx()\n }\n\n summary(): Summary {\n const c = this.db\n .prepare(\n `SELECT COUNT(*) AS sessions,\n COALESCE(SUM(cost_usd),0) AS costUsd,\n COALESCE(SUM(tok_input+tok_output+tok_cache_create+tok_cache_read),0) AS tokens,\n MIN(started_at) AS firstAt, MAX(started_at) AS lastAt\n FROM sessions`,\n )\n .get() as { sessions: number; costUsd: number; tokens: number; firstAt: string | null; lastAt: string | null }\n\n const models = this.db\n .prepare(\n `SELECT value AS model, COUNT(*) AS count\n FROM sessions, json_each(sessions.models)\n GROUP BY value ORDER BY count DESC`,\n )\n .all() as Array<{ model: string; count: number }>\n\n const outcomes = this.db\n .prepare('SELECT type, COUNT(*) AS count FROM outcomes GROUP BY type ORDER BY count DESC')\n .all() as Array<{ type: string; count: number }>\n\n const topTools = this.db\n .prepare(\n 'SELECT name, COUNT(*) AS calls, COALESCE(SUM(is_error),0) AS errors FROM tool_calls GROUP BY name ORDER BY calls DESC LIMIT 10',\n )\n .all() as Array<{ name: string; calls: number; errors: number }>\n\n const analysisCostUsd = (\n this.db.prepare('SELECT COALESCE(SUM(cost_usd),0) AS s FROM processor_runs').get() as { s: number }\n ).s\n\n // Whether LLM enrichment has ever run. LLM-backed processors record their model\n // in processor_runs (non-LLM ones store NULL), so a single non-null model is a\n // durable \"enrichment ran\" signal — independent of which annotation dimensions\n // the enricher currently emits (those can be renamed/removed; this won't). Note\n // a row is only written on success, which is exactly what we want here: \"did\n // enrichment actually produce anything\", not \"was a key merely configured\".\n const enrichmentRan =\n (this.db.prepare('SELECT EXISTS(SELECT 1 FROM processor_runs WHERE model IS NOT NULL) AS r').get() as { r: number })\n .r === 1\n\n const features = this.db\n .prepare(\n `SELECT\n (SELECT COUNT(*) FROM artifacts WHERE kind='feature') AS total,\n (SELECT COUNT(*) FROM artifacts WHERE kind='feature' AND source='derived') AS derived,\n (SELECT COUNT(DISTINCT artifact_id) FROM session_artifacts WHERE role='contributed') AS linked`,\n )\n .get() as { total: number; derived: number; linked: number }\n\n return {\n ...c,\n models,\n outcomes,\n topTools,\n costPerMergedPr: this.costPerArtifact('pr'),\n analysisCostUsd,\n enrichmentRan,\n lastAnalyzedAt: this.getMeta('last_analyze_at') ?? null,\n analyzedRoots: this.db\n .prepare('SELECT source, path, last_analyzed_at AS lastAnalyzedAt FROM analyzed_roots ORDER BY source, path')\n .all() as Summary['analyzedRoots'],\n useCases: this.facetDistribution('use_case'),\n complexity: this.scalarDist('complexity'),\n autonomy: this.scalarDist('autonomy'),\n features,\n }\n }\n\n /** The single most significant week-over-week move to lead the digest with:\n * compares this window's headline KPIs (spend, success rate, session count) to\n * the prior equal-length window and returns the biggest mover — normalized to\n * how many times over its own \"notable\" bar each one is, so one metric type\n * can't dominate just because its natural swings run larger. Null when nothing\n * clears its bar, or the prior window's base is too thin to trust the delta.\n * Only called for a bounded [from, to). */\n private trendHeadline(from: string, to: string): Highlight | null {\n const span = new Date(to).getTime() - new Date(from).getTime()\n const prevFrom = new Date(new Date(from).getTime() - span).toISOString()\n const cur = this.kpis(from, to)\n const prev = this.kpis(prevFrom, from)\n const cands: Array<{ score: number; h: Highlight }> = []\n // Spend: relative change; needs a non-trivial prior base so a $1→$3 blip\n // doesn't read as \"+200%\".\n if (prev.totalSpend >= 5) {\n const pct = ((cur.totalSpend - prev.totalSpend) / prev.totalSpend) * 100\n if (Math.abs(pct) >= 20)\n cands.push({ score: Math.abs(pct) / 20, h: { kind: 'trend', metric: 'spend', cur: cur.totalSpend, prev: prev.totalSpend, pct: Math.round(pct) } })\n }\n // Success rate: percentage-point change; needs enough sessions both sides for\n // the rate to mean anything.\n if (cur.successRate != null && prev.successRate != null && cur.sessions >= 3 && prev.sessions >= 3) {\n const pp = (cur.successRate - prev.successRate) * 100\n if (Math.abs(pp) >= 10)\n cands.push({ score: Math.abs(pp) / 10, h: { kind: 'trend', metric: 'rate', cur: cur.successRate, prev: prev.successRate, pp: Math.round(pp) } })\n }\n // Session count: relative change; needs a non-trivial prior base.\n if (prev.sessions >= 3) {\n const pct = ((cur.sessions - prev.sessions) / prev.sessions) * 100\n if (Math.abs(pct) >= 25)\n cands.push({ score: Math.abs(pct) / 25, h: { kind: 'trend', metric: 'sessions', cur: cur.sessions, prev: prev.sessions, pct: Math.round(pct) } })\n }\n if (!cands.length) return null\n cands.sort((a, b) => b.score - a.score)\n return cands[0]!.h\n }\n\n /** The windowed \"reliable facts\" behind the Highlights digest — most-spend\n * shipped artifact, its stalled (not-yet-shipped) counterpart, converted spend,\n * and the busiest source file — all scoped to [from, to) (omit for all-time) so\n * the whole digest honors the dashboard window. */\n private windowedFacts(from?: string, to?: string) {\n const w = from && to ? ' AND s.started_at >= ? AND s.started_at < ?' : ''\n const wp: string[] = from && to ? [from, to] : []\n const scalar = (sql: string) => (this.db.prepare(sql).get(...wp) as { v: number }).v\n\n const total = scalar(`SELECT COALESCE(SUM(cost_usd),0) AS v FROM sessions s WHERE 1=1${w}`)\n // Converted spend is BLOCK-level: a usage row counts only if its block is linked\n // to a completed artifact (any role — production OR your review of it), so only the\n // parts of a session that touched a shipped artifact count, not the whole session.\n // Matches the burn chart's green band and the cost-per-shipped-artifact basis.\n const shipped = scalar(\n `SELECT COALESCE(SUM(u.cost_usd),0) AS v\n FROM usage_facts u JOIN sessions s ON s.id = u.session_id\n WHERE EXISTS (SELECT 1 FROM block_usage bu\n JOIN block_artifacts ba ON ba.session_id = bu.session_id AND ba.block_idx = bu.block_idx\n JOIN artifacts a ON a.id = ba.artifact_id\n WHERE bu.session_id = u.session_id AND bu.usage_idx = u.idx\n AND a.completed_at IS NOT NULL)${w}`,\n )\n\n // Busiest source file — most distinct sessions, skipping generated noise. We\n // fetch the top two so the digest can require a CLEAR leader (a lone winner,\n // not one of a big pack tied at the same low count — that tie is what made the\n // old \"more than any other\" claim misleading).\n const topFiles = this.db\n .prepare(\n `SELECT fi.path AS path, COUNT(DISTINCT fi.session_id) AS sessions\n FROM files_index fi JOIN sessions s ON s.id = fi.session_id\n WHERE 1=1${w}\n AND fi.path NOT LIKE '%.lock' AND fi.path NOT LIKE '%lock.json'\n AND fi.path NOT LIKE '%lock.yaml' AND fi.path NOT LIKE '%/go.sum'\n GROUP BY fi.repo, fi.path ORDER BY sessions DESC, fi.path LIMIT 2`,\n )\n .all(...wp) as Array<{ path: string; sessions: number }>\n const topFile = topFiles[0]\n ? { path: topFiles[0].path, sessions: topFiles[0].sessions, clearLead: !topFiles[1] || topFiles[0].sessions > topFiles[1].sessions }\n : undefined\n\n // Biggest shipped: shipped feature → merged PR → wip feature, among artifacts\n // active in the window, ranked by block-attributed cost (matches the Artifacts table).\n // Full cost — all roles, production AND your review of it — so a PR you contributed\n // to by reviewing is ranked on its real spend (mirrors costPerArtifact).\n const blockCost = `COALESCE((SELECT SUM(cost_usd) FROM (\n SELECT DISTINCT u.session_id, u.idx AS uidx, u.cost_usd FROM block_artifacts ba\n JOIN block_usage bu ON bu.session_id = ba.session_id AND bu.block_idx = ba.block_idx\n JOIN usage_facts u ON u.session_id = bu.session_id AND u.idx = bu.usage_idx\n WHERE ba.artifact_id = a.id)), 0)`\n // An artifact is \"in window\" (and a candidate at all) via ANY session link in the\n // window — authored or reviewed — so every artifact you contributed to is eligible\n // for the biggest-shipped / stalled spotlight.\n const inWindow =\n from && to\n ? `EXISTS (SELECT 1 FROM session_artifacts sa JOIN sessions s ON s.id = sa.session_id\n WHERE sa.artifact_id = a.id\n AND s.started_at >= ? AND s.started_at < ?)`\n : `EXISTS (SELECT 1 FROM session_artifacts sa WHERE sa.artifact_id = a.id)`\n const pickWin = (kind: string, completion: 'shipped' | 'unshipped') =>\n this.db\n .prepare(\n `SELECT a.title AS title, a.repo AS repo, a.ident AS ident, ${blockCost} AS cost\n FROM artifacts a\n WHERE a.kind = ? AND a.completed_at IS ${completion === 'shipped' ? 'NOT NULL' : 'NULL'} AND ${inWindow}\n ORDER BY cost DESC LIMIT 1`,\n )\n .get(...(from && to ? [kind, from, to] : [kind])) as\n | { title: string; repo: string | null; ident: string | null; cost: number }\n | undefined\n\n type Pick = { kind: 'feature' | 'pr'; title: string; repo: string | null; ident: string | null; cost: number }\n // Most AI spend on SHIPPED work: a shipped feature if the user marks any\n // shipped, else the costliest merged PR. No unshipped fallback — that's what\n // `stalled` is for.\n const shippedFeat = pickWin('feature', 'shipped')\n let spotlight: Pick | null = null\n if (shippedFeat) spotlight = { kind: 'feature', ...shippedFeat }\n else {\n const pr = pickWin('pr', 'shipped')\n if (pr) spotlight = { kind: 'pr', ...pr }\n }\n\n // Most AI spend NOT yet shipped (stalled): an unshipped feature, but only when\n // the user actually marks features shipped (otherwise every feature is\n // trivially \"unshipped\" and it's noise) — else fall back to the costliest\n // open/unmerged PR, a reliable git-derived signal that works for new users.\n let stalled: Pick | null = null\n if (shippedFeat) {\n const uf = pickWin('feature', 'unshipped')\n if (uf) stalled = { kind: 'feature', ...uf }\n } else {\n const up = pickWin('pr', 'unshipped')\n if (up) stalled = { kind: 'pr', ...up }\n }\n\n return { total, shipped, topFile, spotlight, stalled }\n }\n\n /** The Highlights digest: a few reliably-interesting facts plus facet-WALKED\n * comparisons (spend concentration, outcome-rate spread) — for each, we go down\n * an ordered facet list and keep the FIRST facet whose breakdown clears an\n * interestingness threshold, so nothing is hardcoded to `repo` and a dominated\n * split (e.g. one harness at 99%) is skipped. Each insight is a typed payload;\n * the client renders the sentence + drill-in. `from`/`to` window everything;\n * omit for all-time. */\n highlights(from?: string, to?: string): Highlight[] {\n const out: Highlight[] = []\n // A stalled-spend insight only fires when the costliest unshipped artifact is a\n // meaningful slice of the window's spend (so a tiny unshipped feature is quiet).\n const STALLED_MIN_SHARE = 0.15\n const f = this.windowedFacts(from, to)\n\n // --- Week-over-week trend headline (only over a bounded window, where a\n // \"previous window\" exists to compare against) ---\n if (from && to) {\n const trend = this.trendHeadline(from, to)\n if (trend) out.push(trend)\n }\n\n // --- Reliable facts, windowed (show when the data exists) ---\n if (f.spotlight) {\n const sp = f.spotlight\n out.push({ kind: 'biggest_shipped', artifactKind: sp.kind, title: sp.title, repo: sp.repo, ident: sp.ident, cost: sp.cost })\n }\n // The stalled counterpart to biggest_shipped — kept adjacent so they read as a\n // shipped/not-yet-shipped pair.\n if (f.stalled && f.total > 0 && f.stalled.cost / f.total >= STALLED_MIN_SHARE)\n out.push({ kind: 'stalled_spend', artifactKind: f.stalled.kind, title: f.stalled.title, repo: f.stalled.repo, ident: f.stalled.ident, cost: f.stalled.cost })\n if (f.total > 0 && f.shipped > 0)\n out.push({ kind: 'converted_spend', shipped: f.shipped, total: f.total, pct: Math.round((100 * f.shipped) / f.total) })\n // Busiest file — only with a CLEAR leader and real repetition (≥3 sessions), so\n // we never surface an arbitrary file from a big low-count tie.\n if (f.topFile && f.topFile.sessions >= 3 && f.topFile.clearLead)\n out.push({ kind: 'active_file', path: f.topFile.path, sessions: f.topFile.sessions })\n\n // --- Facet-walked: spend concentration (one value leads, but doesn't own it\n // all). use_case is excluded: `implement` dominates almost everyone's spend, so\n // it always won the walk with an uninteresting result. ---\n for (const facet of ['repo', 'model', 'harness']) {\n const r = this.spendOverTime({ bucket: 'month', by: facet, from, to })\n if ('error' in r || !r.series || r.series.length < 2) continue\n const total = (r.overall?.points ?? []).reduce((a, p) => a + p.spend, 0)\n if (total <= 0) continue\n const top = r.series.slice().sort((a, b) => b.total - a.total)[0]\n if (!top || !top.key || top.key === 'Other') continue\n const share = top.total / total\n if (share >= 0.5 && share <= 0.9) {\n out.push({ kind: 'spend_concentration', facet, value: top.key, spend: top.total, total, pct: Math.round(share * 100) })\n break\n }\n }\n\n // --- Facet-walked: outcome-rate spread (a real gap between best and worst value) ---\n // use_case is intentionally excluded: it's multi-valued (a session counts under\n // several work types), so per-value rates mix overlapping populations.\n for (const facet of ['repo', 'complexity', 'model', 'autonomy']) {\n const r = this.successRate({ outcomes: ['session_success'], bucket: 'month', by: facet, from, to })\n const vals = (r.series ?? []).filter((s) => s.denom >= 3 && s.rate != null && s.key && s.key !== 'Other')\n if (vals.length < 2) continue\n const sorted = vals.slice().sort((a, b) => (b.rate as number) - (a.rate as number))\n const best = sorted[0]\n const worst = sorted[sorted.length - 1]\n if (!best || !worst) continue\n if ((best.rate as number) - (worst.rate as number) >= 0.2) {\n out.push({\n kind: 'success_spread',\n facet,\n best: { value: best.key, rate: best.rate, n: best.denom },\n worst: { value: worst.key, rate: worst.rate, n: worst.denom },\n })\n break\n }\n }\n\n // --- Autonomy on complex tasks: a count (enrichment-gated), with a guarded\n // delta. Only shows when enrichment has run AND there are ≥2 such sessions; the\n // \"vs. prior window\" delta is appended ONLY when the prior window has a real\n // base (≥3), so a 1→2 jump never reads as \"+100%\". ---\n if (this.db.prepare(`SELECT 1 FROM annotations WHERE key='autonomy' LIMIT 1`).get()) {\n const countAC = (a?: string, b?: string) =>\n (\n this.db\n .prepare(\n // \"complex\" = the two hardest complexity tiers in the enricher's taxonomy.\n `SELECT COUNT(*) AS n FROM sessions s\n WHERE EXISTS (SELECT 1 FROM annotations an WHERE an.session_id = s.id AND an.key = 'autonomy'\n AND json_extract(an.value,'$') = 'autonomous')\n AND EXISTS (SELECT 1 FROM annotations an WHERE an.session_id = s.id AND an.key = 'complexity'\n AND json_extract(an.value,'$') IN ('substantial','open-ended'))${a && b ? ' AND s.started_at >= ? AND s.started_at < ?' : ''}`,\n )\n .get(...(a && b ? [a, b] : [])) as { n: number }\n ).n\n const cur = countAC(from, to)\n if (cur >= 2) {\n let delta: number | null = null\n if (from && to) {\n const span = new Date(to).getTime() - new Date(from).getTime()\n const prevFrom = new Date(new Date(from).getTime() - span).toISOString()\n const prev = countAC(prevFrom, from)\n if (prev >= 3) delta = Math.round(((cur - prev) / prev) * 100)\n }\n out.push({ kind: 'autonomy_complex', count: cur, delta })\n }\n }\n\n return out\n }\n\n /** Distribution of a scalar annotation value across sessions. */\n private scalarDist(key: string): Dist[] {\n return this.db\n .prepare(\n \"SELECT json_extract(value,'$') AS value, COUNT(*) AS count FROM annotations WHERE key = ? GROUP BY value ORDER BY count DESC\",\n )\n .all(key) as Dist[]\n }\n\n /**\n * Windowed cost-per-shipped-artifact KPI (no window = all time). The numerator is\n * the cost of the BLOCKS that produced each in-window completed artifact (block→PR\n * is deterministic; block→feature is the LLM feature_runs). Blocks partition the\n * session, so a session that also did unshipped/other work is NOT charged whole —\n * the old unique-session approximation dissolves (handling_long_sessions P1/P2).\n * Falls back to whole-session cost for any artifact with NO block links (a feature\n * the model never block-linked, or pre-block data). Both paths are at usage grain\n * and UNION-deduped, so a usage row shared across in-window artifacts counts once.\n */\n costPerArtifact(kind: string, from?: string, to?: string, complexity?: string): { count: number; costPerUnit: number | null } {\n const range = from && to ? 'AND a.completed_at >= ? AND a.completed_at < ?' : ''\n const cxFilter = complexityWhere(complexity, 'a', kind)\n const params = from && to ? [kind, from, to] : [kind]\n // Every shipped artifact you CONTRIBUTED to counts — one you authored OR one you\n // only reviewed (any session link) — as long as it's completed/merged, and its\n // FULL cost (production + your review of it) is charged. So the cost-breakdown\n // treemap, which sums the same per-artifact cost, reconciles with this KPI.\n // (Reviewed role is PRs only; the guard is a no-op for features.)\n const contributed =\n kind === 'pr'\n ? \"AND EXISTS (SELECT 1 FROM session_artifacts spx WHERE spx.artifact_id = a.id)\"\n : ''\n const count = (\n this.db\n .prepare(`SELECT COUNT(*) AS n FROM artifacts a WHERE a.kind = ? AND a.completed_at IS NOT NULL ${range} ${contributed} ${cxFilter}`)\n .get(...params) as { n: number }\n ).n\n if (count === 0) return { count: 0, costPerUnit: null }\n const num = (\n this.db\n .prepare(\n `SELECT COALESCE(SUM(cost_usd),0) AS s FROM (\n -- block-attributed: usage rows in blocks linked to an in-window completed\n -- artifact (all roles — production AND your review of it).\n SELECT DISTINCT u.session_id, u.idx AS uidx, u.cost_usd\n FROM artifacts a\n JOIN block_artifacts ba ON ba.artifact_id = a.id\n JOIN block_usage bu ON bu.session_id = ba.session_id AND bu.block_idx = ba.block_idx\n JOIN usage_facts u ON u.session_id = bu.session_id AND u.idx = bu.usage_idx\n WHERE a.kind = ? AND a.completed_at IS NOT NULL ${range} ${contributed} ${cxFilter}\n )`,\n )\n .get(...params) as { s: number }\n ).s\n return { count, costPerUnit: num / count }\n }\n\n /**\n * The headline KPI row for one time window. Session-grain metrics (count,\n * spend, outcome rate) window by session start; cost-per-artifact windows by\n * completion (see costPerArtifact). The API calls this twice — current and the\n * same-length prior period — to derive deltas. No window = all time.\n */\n kpis(from?: string, to?: string, outcomes?: string[]): KpiSnapshot {\n const range = from && to ? 'WHERE s.started_at >= ? AND s.started_at < ?' : ''\n const params = from && to ? [from, to] : []\n // Which outcome types count as success (the UI's editable definition).\n // Empty → the default. The placeholders sit in the SELECT subquery, so their\n // params bind before the WHERE range params (see successRate's same note).\n const oc = outcomes && outcomes.length ? outcomes : ['session_success']\n const agg = this.db\n .prepare(\n `SELECT COUNT(*) AS sessions,\n COALESCE(SUM(s.cost_usd),0) AS totalSpend,\n AVG(CASE WHEN EXISTS (\n SELECT 1 FROM outcomes o\n WHERE o.session_id = s.id AND o.type IN (${oc.map(() => '?').join(',')})\n ) THEN 1.0 ELSE 0.0 END) AS successRate\n FROM sessions s ${range}`,\n )\n .get(...oc, ...params) as { sessions: number; totalSpend: number; successRate: number | null }\n // Tool-call error rate over the same session-start window (tool calls join up\n // to their session for the time basis, consistent with the other KPIs).\n const tc = this.db\n .prepare(\n `SELECT COUNT(*) AS calls, COALESCE(SUM(t.is_error),0) AS errs\n FROM tool_calls t JOIN sessions s ON s.id = t.session_id ${range}`,\n )\n .get(...params) as { calls: number; errs: number }\n return {\n sessions: agg.sessions,\n totalSpend: agg.totalSpend,\n // Null (not 0) when the window has no sessions, so the UI shows \"—\" not \"0%\".\n successRate: agg.sessions ? (agg.successRate ?? 0) : null,\n errorRate: tc.calls ? tc.errs / tc.calls : null,\n costPerFeature: this.costPerArtifact('feature', from, to),\n costPerPr: this.costPerArtifact('pr', from, to),\n }\n }\n\n /**\n * The two decomposition curves for the cost-per-artifact section\n * (cost_per_shipped_artifact.md). Both are PURE SUMS (0 is a real value, no\n * attribution): burn = AI spend per bucket dated at SESSION time (with a\n * `shippedSpend` sub-band = spend of sessions linked to a completed `kind`\n * artifact — the gap to `spend` is in-flight/never-shipped spend); throughput\n * = count of `kind` artifacts per bucket dated at COMPLETION. Both honor the\n * optional window (burn by session start, throughput by completion); no window\n * = full history. The `bucket` granularity is the caller's (day/week/month).\n */\n costCurves(\n kind: string,\n bucket: Bucket,\n from?: string,\n to?: string,\n complexity?: string,\n ): {\n burn: Array<{ bucket: string; spend: number; shippedSpend: number }>\n throughput: Array<{ bucket: string; count: number }>\n /** PRs reviewed per bucket, dated at REVIEW time (the pr_reviewed outcome ts). PRs only. */\n reviewed: Array<{ bucket: string; count: number }>\n buckets: string[]\n } {\n // Anchored on usage_facts and dated at message time (COALESCE u.ts → session\n // start), so spend buckets at block-level time granularity (P5), and the\n // converted sub-band greens the BLOCKS linked to a shipped artifact (any role —\n // production or review), not the whole session. kind binds first (CASE), then\n // window. Throughput counts every completed artifact you CONTRIBUTED to (any\n // session link — authored or reviewed); PRs only, no-op for features.\n const contributed =\n kind === 'pr'\n ? \"AND EXISTS (SELECT 1 FROM session_artifacts spx WHERE spx.artifact_id = artifacts.id)\"\n : ''\n const cxFilter = complexityWhere(complexity, 'a', kind) // for burn subquery (alias `a`)\n const cxFilterBare = complexityWhere(complexity, 'artifacts', kind) // for throughput (bare table name)\n const burnRange = from && to ? 'AND COALESCE(u.ts, s.started_at) >= ? AND COALESCE(u.ts, s.started_at) < ?' : ''\n const burnParams = from && to ? [kind, from, to] : [kind]\n const burn = this.db\n .prepare(\n `SELECT ${bucketExpr('COALESCE(u.ts, s.started_at)', bucket)} AS bucket,\n COALESCE(SUM(u.cost_usd),0) AS spend,\n COALESCE(SUM(CASE WHEN EXISTS (\n SELECT 1 FROM block_usage bu\n JOIN block_artifacts ba ON ba.session_id = bu.session_id AND ba.block_idx = bu.block_idx\n JOIN artifacts a ON a.id = ba.artifact_id\n WHERE bu.session_id = u.session_id AND bu.usage_idx = u.idx\n AND a.kind = ? AND a.completed_at IS NOT NULL ${cxFilter}\n ) THEN u.cost_usd ELSE 0 END),0) AS shippedSpend\n FROM usage_facts u JOIN sessions s ON s.id = u.session_id\n WHERE COALESCE(u.ts, s.started_at) IS NOT NULL ${burnRange}\n GROUP BY bucket ORDER BY bucket`,\n )\n .all(...burnParams) as Array<{ bucket: string; spend: number; shippedSpend: number }>\n const thRange = from && to ? 'AND completed_at >= ? AND completed_at < ?' : ''\n const thParams = from && to ? [kind, from, to] : [kind]\n const throughput = this.db\n .prepare(\n `SELECT ${bucketExpr('completed_at', bucket)} AS bucket, COUNT(*) AS count\n FROM artifacts WHERE kind = ? AND completed_at IS NOT NULL ${thRange} ${contributed} ${cxFilterBare} GROUP BY bucket ORDER BY bucket`,\n )\n .all(...thParams) as Array<{ bucket: string; count: number }>\n // PRs reviewed per bucket, dated at REVIEW time (when you reviewed, not when the\n // PR merged) — sourced from the pr_reviewed outcome. Distinct PRs, so two review\n // sessions on the same PR count once. Features have no review signal → empty.\n const revRange = from && to ? 'AND o.ts >= ? AND o.ts < ?' : ''\n const cxFilterRev = complexityWhere(complexity, 'a', kind)\n const reviewed =\n kind === 'pr'\n ? (this.db\n .prepare(\n `SELECT ${bucketExpr('o.ts', bucket)} AS bucket, COUNT(DISTINCT o.artifact_id) AS count\n FROM outcomes o\n ${cxFilterRev ? 'JOIN artifacts a ON a.id = o.artifact_id' : ''}\n WHERE o.type = 'pr_reviewed' AND o.ts IS NOT NULL ${revRange} ${cxFilterRev}\n GROUP BY bucket ORDER BY bucket`,\n )\n .all(...(from && to ? [from, to] : [])) as Array<{ bucket: string; count: number }>)\n : []\n // The x-axis: over a window, every bucket from `from` to `to` (so empty\n // periods show as gaps and the chart spans the whole window, not just the\n // periods that happen to have data); all-time falls back to the data's own\n // buckets. The series rows stay sparse — the chart zero-fills missing axis\n // buckets — and we still union in any data bucket as a safety net.\n const set = new Set<string>()\n if (from && to) this.bucketAxis(from, to, bucket).forEach((b) => set.add(b))\n burn.forEach((r) => set.add(r.bucket))\n throughput.forEach((r) => set.add(r.bucket))\n reviewed.forEach((r) => set.add(r.bucket))\n return { burn, throughput, reviewed, buckets: Array.from(set).sort() }\n }\n\n /**\n * The complete ordered list of bucket labels spanning [from, to] at the given\n * granularity. Walks one calendar day at a time in SQL and buckets each with\n * the same expression as the data, so the labels match exactly (no JS attempt\n * to reproduce SQLite's %W week numbering). Used to give the cost curves a\n * continuous x-axis across the window.\n */\n private bucketAxis(from: string, to: string, bucket: Bucket): string[] {\n const rows = this.db\n .prepare(\n `WITH RECURSIVE days(d) AS (\n SELECT date(?)\n UNION ALL\n SELECT date(d, '+1 day') FROM days WHERE d < date(?)\n )\n SELECT DISTINCT ${bucketExpr('d', bucket)} AS bucket FROM days ORDER BY bucket`,\n )\n .all(from, to) as Array<{ bucket: string }>\n return rows.map((r) => r.bucket)\n }\n\n /**\n * The x-axis for a windowed time series: every bucket from `from` to `to`\n * (so the chart spans the whole window and empty periods show as gaps),\n * unioned with the data's own buckets as a safety net. No window → the data's\n * buckets as-is (all-time). Shared by the dashboard time-series endpoints.\n */\n private fullAxis(dataBuckets: string[], bucket: Bucket, from?: string, to?: string): string[] {\n if (!(from && to)) return dataBuckets\n const set = new Set<string>(this.bucketAxis(from, to, bucket))\n dataBuckets.forEach((b) => set.add(b))\n return Array.from(set).sort()\n }\n\n /**\n * The \"burn efficiency\" lens for a window: Σ session spend in the window ÷\n * count of `kind` artifacts completed in the window. Deliberately distinct\n * from the unit-cost KPI (whose numerator includes pre-window spend) — the doc\n * insists both be shown so dividing the curves doesn't read as a contradiction.\n * `throughput` here equals the KPI denominator exactly. No window = all time.\n */\n costPeriod(kind: string, from?: string, to?: string, complexity?: string): { burn: number; throughput: number; efficiency: number | null } {\n const burnRange = from && to ? 'WHERE started_at >= ? AND started_at < ?' : 'WHERE started_at IS NOT NULL'\n const burnParams = from && to ? [from, to] : []\n const burn = (\n this.db.prepare(`SELECT COALESCE(SUM(cost_usd),0) AS s FROM sessions ${burnRange}`).get(...burnParams) as {\n s: number\n }\n ).s\n const thRange = from && to ? 'AND completed_at >= ? AND completed_at < ?' : ''\n const thParams = from && to ? [kind, from, to] : [kind]\n // Same \"contributed\" guard as costPerArtifact, so this throughput (the KPI\n // denominator) matches the cost-per-shipped count exactly: every completed\n // artifact you have a session link to, authored or reviewed (PRs only).\n const contributed =\n kind === 'pr'\n ? \"AND EXISTS (SELECT 1 FROM session_artifacts spx WHERE spx.artifact_id = artifacts.id)\"\n : ''\n const cxFilter = complexityWhere(complexity, 'artifacts', kind)\n const throughput = (\n this.db\n .prepare(`SELECT COUNT(*) AS n FROM artifacts WHERE kind = ? AND completed_at IS NOT NULL ${thRange} ${contributed} ${cxFilter}`)\n .get(...thParams) as { n: number }\n ).n\n return { burn, throughput, efficiency: throughput ? burn / throughput : null }\n }\n\n /**\n * Spend over time, optionally split into one series per facet value — the doc's\n * non-headline \"total spend breakdown\" / burn view (cost_per_shipped_artifact.md\n * §Separate). Anchored on usage_facts so cost-by-model splits HONESTLY (each\n * usage row attributed to its own model), not by charging a multi-model\n * session's whole cost to each model. Spend is dated at session start (matching\n * the burn curve). Only usage/session-grain facets are valid (the cost measure's\n * grain guard); tool-call facets (skill) are rejected. Multi-valued facets\n * (use_case) presence-inflate — flagged via `presenceInflated`.\n */\n spendOverTime(q: SpendOverTimeQuery): SpendOverTimeResult | { error: string } {\n const bucket = q.bucket\n const topK = q.topK ?? 6\n const time = bucketExpr('s.started_at', bucket)\n\n const where: string[] = ['s.started_at IS NOT NULL']\n const params: unknown[] = []\n if (q.from && q.to) {\n where.push('s.started_at >= ? AND s.started_at < ?')\n params.push(q.from, q.to)\n }\n for (const [k, v] of Object.entries(q.filters ?? {})) {\n if (!v) continue\n const spec = this.facet(k)\n if (!spec) continue\n const p = this.facetPredicate(spec, v)\n where.push(p.sql)\n params.push(...p.params)\n }\n const fromSql = 'FROM usage_facts u JOIN sessions s ON s.id = u.session_id'\n const whereSql = 'WHERE ' + where.join(' AND ')\n\n const overallRows = this.db\n .prepare(`SELECT ${time} AS tb, SUM(u.cost_usd) AS spend ${fromSql} ${whereSql} GROUP BY tb ORDER BY tb`)\n .all(...params) as Array<{ tb: string; spend: number }>\n const overallPoints = overallRows.map((r) => ({ bucket: r.tb, spend: r.spend }))\n const overall = { points: overallPoints, total: overallPoints.reduce((a, p) => a + p.spend, 0) }\n\n let series: SpendSeries[] | undefined\n let truncated: { shown: number; total: number } | undefined\n let presenceInflated: boolean | undefined\n if (q.by) {\n const f = this.facet(q.by)\n if (!f) return { error: 'unknown facet' }\n const gf = grainOf(f.source)\n if (!facetGroupCompatible(gf, 'usage')) return { error: 'incompatible grain' }\n const fg = facetGroupExpr(f, 'usage')\n const sql = `SELECT ${time} AS tb, ${fg.expr} AS val, SUM(u.cost_usd) AS spend\n ${fromSql} ${fg.join} ${whereSql}${fg.where ? ' AND ' + fg.where : ''}\n GROUP BY tb, val`\n const rows = this.db.prepare(sql).all(...params) as Array<{ tb: string; val: string | null; spend: number }>\n const byVal = new Map<string, SpendPoint[]>()\n for (const r of rows) {\n if (r.val == null) continue\n const arr = byVal.get(String(r.val)) ?? []\n arr.push({ bucket: r.tb, spend: r.spend })\n byVal.set(String(r.val), arr)\n }\n let all: SpendSeries[] = Array.from(byVal.entries()).map(([key, points]) => ({\n key,\n points,\n total: points.reduce((a, p) => a + p.spend, 0),\n }))\n all.sort((a, b) => b.total - a.total)\n if (all.length > topK) {\n truncated = { shown: topK, total: all.length }\n all = all.slice(0, topK)\n }\n series = all\n presenceInflated = !!f.multi\n }\n\n return { bucket, buckets: this.fullAxis(overallPoints.map((p) => p.bucket), bucket, q.from, q.to), overall, series, truncated, presenceInflated }\n }\n\n /**\n * Session COUNT over time, optionally split into one series per COMPOSITE label\n * — the time-series form of the distribution cards. Each session is labeled by\n * the sorted set of its distinct values for the dimension (e.g. <opus, haiku>)\n * and grouped by it, so every session lands in exactly one series and the\n * counts partition the total (honest to STACK) — no presence-inflation. The\n * tail past top-K collapses into \"Other\".\n */\n sessionsOverTime(q: SessionsOverTimeQuery): SessionsOverTimeResult {\n const bucket = q.bucket\n const topK = q.topK ?? 6\n const time = bucketExpr('s.started_at', bucket)\n\n const baseWhere: string[] = ['s.started_at IS NOT NULL']\n const baseParams: unknown[] = []\n if (q.from && q.to) {\n baseWhere.push('s.started_at >= ? AND s.started_at < ?')\n baseParams.push(q.from, q.to)\n }\n for (const [k, v] of Object.entries(q.filters ?? {})) {\n if (!v) continue\n const spec = this.facet(k)\n if (!spec) continue\n const p = this.facetPredicate(spec, v)\n baseWhere.push(p.sql)\n baseParams.push(...p.params)\n }\n\n const runBuckets = (extraSql: string, extraParams: unknown[]): CountPoint[] => {\n const where = [...baseWhere]\n const params = [...baseParams]\n if (extraSql) {\n where.push(extraSql)\n params.push(...extraParams)\n }\n const rows = this.db\n .prepare(`SELECT ${time} AS tb, COUNT(*) AS cnt FROM sessions s WHERE ${where.join(' AND ')} GROUP BY tb ORDER BY tb`)\n .all(...params) as Array<{ tb: string; cnt: number }>\n return rows.map((r) => ({ bucket: r.tb, count: r.cnt }))\n }\n const totals = (pts: CountPoint[]) => pts.reduce((a, p) => a + p.count, 0)\n\n const overallPoints = runBuckets('', [])\n const overall = { points: overallPoints, total: totals(overallPoints) }\n\n let series: CountSeries[] | undefined\n let truncated: { shown: number; total: number } | undefined\n if (q.by) {\n const f = this.facet(q.by)\n if (f) {\n // Composite breakdown (mirrors successRate): GROUP BY (bucket, value-set),\n // so each session is counted in exactly one series → the stack is honest.\n const combo = this.comboExpr(f)\n const params: unknown[] = [...combo.params, ...baseParams]\n const rows = this.db\n .prepare(\n `SELECT tb, combo, COUNT(*) AS cnt FROM (\n SELECT ${time} AS tb, ${combo.sql} AS combo\n FROM sessions s WHERE ${baseWhere.join(' AND ')}\n ) t GROUP BY tb, combo ORDER BY tb`,\n )\n .all(...params) as Array<{ tb: string; combo: string | null; cnt: number }>\n\n // Order members within a combo by global volume (primary-first), which\n // also canonicalizes the key for grouping.\n const rank = new Map<string, number>()\n this.facetDistribution(q.by).forEach((d, i) => { if (d.value != null) rank.set(String(d.value), i) })\n const orderCombo = (c: string): string =>\n c.split(', ').sort((a, b) => (rank.get(a) ?? 1e9) - (rank.get(b) ?? 1e9) || (a < b ? -1 : 1)).join(', ')\n\n const byCombo = new Map<string, CountPoint[]>()\n for (const r of rows) {\n const label = !r.combo ? '(none)' : orderCombo(r.combo)\n const pts = byCombo.get(label) ?? []\n pts.push({ bucket: r.tb, count: r.cnt })\n byCombo.set(label, pts)\n }\n let all: CountSeries[] = [...byCombo.entries()]\n .map(([key, points]) => ({ key, points, total: totals(points) }))\n .sort((a, b) => b.total - a.total)\n\n if (all.length > topK) {\n // Collapse the tail into \"Other\" so the stack still sums to the total.\n truncated = { shown: topK, total: all.length }\n const otherByBucket = new Map<string, number>()\n for (const s of all.slice(topK)) {\n for (const p of s.points) otherByBucket.set(p.bucket, (otherByBucket.get(p.bucket) ?? 0) + p.count)\n }\n const otherPts = [...otherByBucket.entries()].map(([bucket, count]) => ({ bucket, count }))\n all = [...all.slice(0, topK), { key: 'Other', points: otherPts, total: totals(otherPts) }]\n }\n series = all\n }\n }\n\n return { bucket, buckets: this.fullAxis(overallPoints.map((p) => p.bucket), bucket, q.from, q.to), overall, series, truncated }\n }\n\n /**\n * Operational tool-call metrics over time. One anchor (tool_calls t JOIN\n * sessions s, dated at session start); the `view` selects what to plot:\n * tool_calls = COUNT(*), error_rate = SUM(is_error)/COUNT(*), skill_usage =\n * COUNT(*) WHERE action='skill'. `by:'name'` splits by tool_calls.name (tool\n * name in general; skill name when skills-only), ranked top-K by call volume.\n */\n opsOverTime(q: OpsOverTimeQuery): OpsOverTimeResult {\n const bucket = q.bucket\n const topK = q.topK ?? 6\n const isRate = q.view === 'error_rate'\n const time = bucketExpr('s.started_at', bucket)\n\n const where: string[] = ['s.started_at IS NOT NULL']\n const params: unknown[] = []\n if (q.view === 'skill_usage') where.push(\"t.action = 'skill'\")\n if (q.from && q.to) {\n where.push('s.started_at >= ? AND s.started_at < ?')\n params.push(q.from, q.to)\n }\n for (const [k, v] of Object.entries(q.filters ?? {})) {\n if (!v) continue\n const spec = this.facet(k)\n if (!spec) continue\n const p = this.facetPredicate(spec, v)\n where.push(p.sql)\n params.push(...p.params)\n }\n // Row-level tool-name scope: restrict which calls are aggregated, so the rate\n // becomes that tool's own (denominator + numerator both shrink to these tools).\n const toolVals = (q.toolNames ?? []).filter(Boolean)\n if (toolVals.length) {\n where.push(`t.name IN (${toolVals.map(() => '?').join(', ')})`)\n params.push(...toolVals)\n }\n const fromSql = 'FROM tool_calls t JOIN sessions s ON s.id = t.session_id'\n const whereSql = 'WHERE ' + where.join(' AND ')\n // Row-level error-category scope: redefine the numerator (which errors count)\n // without touching the denominator — \"rate of <these categories> among all\n // in-scope calls\". These params bind in the SELECT list, AHEAD of WHERE params.\n const catVals = (q.errorCategories ?? []).filter(Boolean)\n const errPh = catVals.map(() => '?').join(', ')\n const errExpr = catVals.length\n ? `COALESCE(SUM(CASE WHEN t.error_category IN (${errPh}) THEN 1 ELSE 0 END), 0)`\n : 'COALESCE(SUM(t.is_error), 0)'\n const val = (cnt: number, errs: number) => (isRate ? (cnt ? errs / cnt : null) : cnt)\n\n const overallRows = this.db\n .prepare(`SELECT ${time} AS tb, COUNT(*) AS cnt, ${errExpr} AS errs ${fromSql} ${whereSql} GROUP BY tb ORDER BY tb`)\n .all(...catVals, ...params) as Array<{ tb: string; cnt: number; errs: number }>\n const overallPoints: OpsPoint[] = overallRows.map((r) => ({\n bucket: r.tb,\n value: val(r.cnt, r.errs),\n calls: r.cnt,\n errors: r.errs,\n }))\n const tCnt = overallRows.reduce((a, r) => a + r.cnt, 0)\n const tErr = overallRows.reduce((a, r) => a + r.errs, 0)\n const overall = { points: overallPoints, total: val(tCnt, tErr) }\n\n let series: OpsSeries[] | undefined\n let truncated: { shown: number; total: number } | undefined\n if (q.by === 'name') {\n const rows = this.db\n .prepare(`SELECT ${time} AS tb, t.name AS nm, COUNT(*) AS cnt, ${errExpr} AS errs ${fromSql} ${whereSql} GROUP BY tb, nm`)\n .all(...catVals, ...params) as Array<{ tb: string; nm: string | null; cnt: number; errs: number }>\n const byVal = new Map<string, { points: OpsPoint[]; cnt: number; errs: number }>()\n for (const r of rows) {\n if (r.nm == null) continue\n const e = byVal.get(r.nm) ?? { points: [], cnt: 0, errs: 0 }\n e.points.push({ bucket: r.tb, value: val(r.cnt, r.errs), calls: r.cnt, errors: r.errs })\n e.cnt += r.cnt\n e.errs += r.errs\n byVal.set(r.nm, e)\n }\n let all: OpsSeries[] = Array.from(byVal.entries()).map(([key, e]) => ({\n key,\n points: e.points,\n total: val(e.cnt, e.errs),\n calls: e.cnt,\n }))\n all.sort((a, b) => b.calls - a.calls) // rank by call volume — the tools that matter\n if (all.length > topK) {\n truncated = { shown: topK, total: all.length }\n all = all.slice(0, topK)\n }\n series = all\n } else if (q.by === 'error_category') {\n // Decompose the error rate by category: each line is a category's errored\n // calls over ALL in-scope calls that bucket, so the lines sum to the overall\n // rate. (A per-category denominator would be a flat 100% — the category\n // column only exists on errored rows.) Honest only for the rate view.\n const totalByBucket = new Map(overallRows.map((r) => [r.tb, r.cnt]))\n const catWhere =\n whereSql + ' AND t.error_category IS NOT NULL' + (catVals.length ? ` AND t.error_category IN (${errPh})` : '')\n const rows = this.db\n .prepare(`SELECT ${time} AS tb, t.error_category AS cat, COUNT(*) AS errs ${fromSql} ${catWhere} GROUP BY tb, cat`)\n .all(...params, ...catVals) as Array<{ tb: string; cat: string | null; errs: number }>\n const catLabel = new Map(ERROR_CATEGORIES.map((c) => [c.key, c.label]))\n const byCat = new Map<string, { points: OpsPoint[]; errs: number }>()\n for (const r of rows) {\n if (r.cat == null) continue\n const denom = totalByBucket.get(r.tb) ?? 0\n const e = byCat.get(r.cat) ?? { points: [], errs: 0 }\n e.points.push({ bucket: r.tb, value: denom ? r.errs / denom : null, calls: denom, errors: r.errs })\n e.errs += r.errs\n byCat.set(r.cat, e)\n }\n let all: OpsSeries[] = Array.from(byCat.entries()).map(([key, e]) => ({\n key,\n label: catLabel.get(key) ?? key,\n points: e.points,\n total: tCnt ? e.errs / tCnt : null,\n calls: e.errs, // rank categories by error volume\n }))\n all.sort((a, b) => b.calls - a.calls)\n if (all.length > topK) {\n truncated = { shown: topK, total: all.length }\n all = all.slice(0, topK)\n }\n series = all\n }\n\n return { view: q.view, bucket, by: q.by, buckets: this.fullAxis(overallPoints.map((p) => p.bucket), bucket, q.from, q.to), overall, series, truncated, format: isRate ? 'pct' : 'int' }\n }\n\n /** Distinct tool-call names, busiest first — feeds the Ops error-rate tool filter. */\n toolNames(): string[] {\n return (\n this.db\n .prepare('SELECT name FROM tool_calls WHERE name IS NOT NULL GROUP BY name ORDER BY COUNT(*) DESC')\n .all() as Array<{ name: string }>\n ).map((r) => r.name)\n }\n\n /**\n * Outcome types present in the data, with the count of distinct sessions that\n * produced each — feeds the success-rate \"what counts as success\" selector.\n * (A first-class outcome-type registry, parallel to facets/measures, is a\n * deferred follow-up; for now the selector reflects what's actually in the DB.)\n */\n outcomeTypes(): Array<{ type: string; sessions: number }> {\n return this.db\n .prepare(\n 'SELECT type, COUNT(DISTINCT session_id) AS sessions FROM outcomes GROUP BY type ORDER BY sessions DESC',\n )\n .all() as Array<{ type: string; sessions: number }>\n }\n\n /**\n * Session Outcome Rate over time (headline_metrics.md): the fraction of\n * sessions — cohorted by START date — that produced any outcome in the\n * selected set. Numerator = sessions with an outcome in `outcomes`; denominator\n * = all sessions in the bucket. Session-level filters apply to BOTH (so the\n * rate is honest). With `by`, returns one series per COMPOSITE label (top-K by\n * volume, the tail collapsed into \"Other\"): each session is labeled by the\n * sorted set of its distinct values for the dimension (e.g. <opus, haiku>), so\n * every session falls in exactly one series and the bars partition the\n * population — multi-valued sessions are counted once, not fanned out.\n */\n successRate(q: SuccessRateQuery): SuccessRateResult {\n const outcomes = q.outcomes.length ? q.outcomes : ['session_success']\n const bucket = q.bucket\n const topK = q.topK ?? 6\n const numPred = `EXISTS (SELECT 1 FROM outcomes o WHERE o.session_id = s.id AND o.type IN (${outcomes\n .map(() => '?')\n .join(',')}))`\n\n // Filter clauses shared by numerator and denominator (window + session facets).\n const filterClauses: string[] = []\n const filterParams: unknown[] = []\n if (q.from && q.to) {\n filterClauses.push('s.started_at >= ? AND s.started_at < ?')\n filterParams.push(q.from, q.to)\n }\n for (const [k, v] of Object.entries(q.filters ?? {})) {\n if (!v) continue\n const spec = this.facet(k)\n if (!spec) continue\n const p = this.facetPredicate(spec, v)\n filterClauses.push(p.sql)\n filterParams.push(...p.params)\n }\n\n // Run the bucketed num/denom for the base population plus an optional extra\n // (per-series) predicate. Param order follows SQL text: numPred's outcome\n // params sit in the SELECT, so they bind before the WHERE params.\n const runBuckets = (extraSql: string, extraParams: unknown[]): RatePoint[] => {\n const where = ['s.started_at IS NOT NULL', ...filterClauses]\n const params: unknown[] = [...outcomes, ...filterParams]\n if (extraSql) {\n where.push(extraSql)\n params.push(...extraParams)\n }\n const sql = `SELECT ${bucketExpr('s.started_at', bucket)} AS bucket,\n COUNT(*) AS denom,\n SUM(CASE WHEN ${numPred} THEN 1 ELSE 0 END) AS num,\n COALESCE(SUM(s.cost_usd),0) AS spend\n FROM sessions s WHERE ${where.join(' AND ')}\n GROUP BY bucket ORDER BY bucket`\n const rows = this.db.prepare(sql).all(...params) as Array<{ bucket: string; denom: number; num: number; spend: number }>\n return rows.map((r) => ({ bucket: r.bucket, num: r.num, denom: r.denom, spend: r.spend, rate: r.denom ? r.num / r.denom : null }))\n }\n\n const totals = (points: RatePoint[]) => {\n const num = points.reduce((a, p) => a + p.num, 0)\n const denom = points.reduce((a, p) => a + p.denom, 0)\n const spend = points.reduce((a, p) => a + p.spend, 0)\n return { num, denom, spend, rate: denom ? num / denom : null }\n }\n\n const overallPoints = runBuckets('', [])\n const overall: RateSeries = { key: 'overall', points: overallPoints, ...totals(overallPoints) }\n\n let series: RateSeries[] | undefined\n let truncated: { shown: number; total: number } | undefined\n if (q.by) {\n const f = this.facet(q.by)\n if (f) {\n // Composite breakdown: label each session by the sorted SET of its distinct\n // values (comboExpr is a correlated subquery yielding a \", \"-joined string,\n // NULL when empty), then GROUP BY (bucket, combo). Every session lands in\n // exactly one combo, so the bars partition the population — no double-count.\n const combo = this.comboExpr(f)\n const where = ['s.started_at IS NOT NULL', ...filterClauses]\n // SELECT-text param order: combo params, then numPred outcomes, then WHERE filters.\n const params: unknown[] = [...combo.params, ...outcomes, ...filterParams]\n const sql = `SELECT bucket, combo, COUNT(*) AS denom, SUM(has_outcome) AS num, COALESCE(SUM(cost),0) AS spend FROM (\n SELECT ${bucketExpr('s.started_at', bucket)} AS bucket,\n ${combo.sql} AS combo,\n (CASE WHEN ${numPred} THEN 1 ELSE 0 END) AS has_outcome,\n s.cost_usd AS cost\n FROM sessions s WHERE ${where.join(' AND ')}\n ) t GROUP BY bucket, combo ORDER BY bucket`\n const rows = this.db.prepare(sql).all(...params) as Array<{ bucket: string; combo: string | null; denom: number; num: number; spend: number }>\n\n // Order values WITHIN a combo by global volume so labels read primary-first\n // (opus before haiku), independent of the SQL key's alpha order. Re-sorting\n // in JS also canonicalizes the key, merging any combos SQLite emitted in a\n // different member order.\n const rank = new Map<string, number>()\n this.facetDistribution(q.by).forEach((d, i) => { if (d.value != null) rank.set(String(d.value), i) })\n const orderCombo = (c: string): string =>\n c.split(', ').sort((a, b) => (rank.get(a) ?? 1e9) - (rank.get(b) ?? 1e9) || (a < b ? -1 : 1)).join(', ')\n\n const byCombo = new Map<string, RatePoint[]>()\n for (const r of rows) {\n const label = !r.combo ? '(none)' : orderCombo(r.combo)\n const pts = byCombo.get(label) ?? []\n pts.push({ bucket: r.bucket, num: r.num, denom: r.denom, spend: r.spend, rate: r.denom ? r.num / r.denom : null })\n byCombo.set(label, pts)\n }\n let all: RateSeries[] = [...byCombo.entries()]\n .map(([key, points]) => ({ key, points, ...totals(points) }))\n .sort((a, b) => b.denom - a.denom)\n\n if (all.length > topK) {\n // Collapse the long tail into a single \"Other\" series so bars still sum\n // to the bucket total; the client offers \"Show all\" to expand it.\n truncated = { shown: topK, total: all.length }\n const otherByBucket = new Map<string, RatePoint>()\n for (const s of all.slice(topK)) {\n for (const p of s.points) {\n const acc = otherByBucket.get(p.bucket) ?? { bucket: p.bucket, num: 0, denom: 0, spend: 0, rate: null }\n acc.num += p.num\n acc.denom += p.denom\n acc.spend += p.spend\n otherByBucket.set(p.bucket, acc)\n }\n }\n const otherPts = [...otherByBucket.values()].map((p) => ({ ...p, rate: p.denom ? p.num / p.denom : null }))\n all = [...all.slice(0, topK), { key: 'Other', points: otherPts, ...totals(otherPts) }]\n }\n series = all\n }\n }\n\n return { outcomes, bucket, buckets: this.fullAxis(overallPoints.map((p) => p.bucket), bucket, q.from, q.to), overall, series, truncated }\n }\n\n // ---- dashboard read API ---------------------------------------------------\n\n /** Spend, session count, and shipped-PR count per time bucket. */\n timeseries(bucket: Bucket, from?: string, to?: string): TimePoint[] {\n const bx = bucketExpr('started_at', bucket)\n const where = from && to ? 'WHERE started_at >= ? AND started_at < ?' : 'WHERE started_at IS NOT NULL'\n const params = from && to ? [from, to] : []\n const spend = this.db\n .prepare(\n `SELECT ${bx} AS bucket, COUNT(*) AS sessions, COALESCE(SUM(cost_usd),0) AS spend\n FROM sessions ${where} GROUP BY bucket ORDER BY bucket`,\n )\n .all(...params) as Array<{ bucket: string; sessions: number; spend: number }>\n\n const shipped = this.db\n .prepare(\n `SELECT ${bucketExpr('completed_at', bucket)} AS bucket, COUNT(*) AS shipped\n FROM artifacts WHERE kind='pr' AND completed_at IS NOT NULL GROUP BY bucket`,\n )\n .all() as Array<{ bucket: string; shipped: number }>\n const shippedMap = new Map(shipped.map((r) => [r.bucket, r.shipped]))\n return spend.map((r) => ({ ...r, shipped: shippedMap.get(r.bucket) ?? 0 }))\n }\n\n /** The facet registry — drives dist cards, filters, and (later) breakdowns. */\n facetList(): FacetSpec[] {\n const rows = this.db.prepare('SELECT * FROM facets ORDER BY key').all() as Array<Record<string, any>>\n return rows.map(rowToFacet)\n }\n\n facet(key: string): FacetSpec | undefined {\n const row = this.db.prepare('SELECT * FROM facets WHERE key = ?').get(key) as Record<string, any> | undefined\n return row ? rowToFacet(row) : undefined\n }\n\n /**\n * Sessions per value of a facet — the generic dist card. The read shape is\n * derived from (source, multi): raw column, json_each, json_extract, or a child\n * table. This is a COUNT, so exploding a multi-valued facet is safe (a session\n * present under two values is intended); SUM measures are a separate concern.\n */\n facetDistribution(key: string): Dist[] {\n const f = this.facet(key)\n if (!f) return []\n const col = f.column ?? f.key\n let sql: string\n const params: unknown[] = []\n if (f.source === 'session') {\n sql = f.multi\n ? `SELECT je.value AS value, COUNT(*) AS count\n FROM sessions s, json_each(s.${col}) je GROUP BY je.value ORDER BY count DESC`\n : `SELECT s.${col} AS value, COUNT(*) AS count\n FROM sessions s WHERE s.${col} IS NOT NULL GROUP BY s.${col} ORDER BY count DESC`\n } else if (f.source === 'annotation') {\n sql = f.multi\n ? `SELECT je.value AS value, COUNT(*) AS count\n FROM annotations a, json_each(a.value) je WHERE a.key = ? GROUP BY je.value ORDER BY count DESC`\n : `SELECT json_extract(a.value,'$') AS value, COUNT(*) AS count\n FROM annotations a WHERE a.key = ? GROUP BY value ORDER BY count DESC`\n params.push(f.key)\n } else if (f.source === 'block') {\n // sessions that have a block labeled value (single label per block)\n sql = `SELECT json_extract(value,'$') AS value, COUNT(DISTINCT session_id) AS count\n FROM block_annotations WHERE key = ? GROUP BY value ORDER BY count DESC`\n params.push(f.key)\n } else {\n const table = f.source === 'usage' ? 'usage_facts' : 'tool_calls'\n const where = f.base ? `WHERE ${f.base}` : ''\n sql = `SELECT ${col} AS value, COUNT(DISTINCT session_id) AS count\n FROM ${table} ${where} GROUP BY ${col} ORDER BY count DESC`\n }\n sql += ' LIMIT 50' // bound high-cardinality facets defensively\n return this.db.prepare(sql).all(...params) as Dist[]\n }\n\n /**\n * Compile a facet + value into a session-scoped boolean SQL fragment (alias `s`).\n * One compiler, reused by session filters today and cohort splits later. Column\n * identifiers and `base` are registry-defined (trusted); the value is a bound param.\n */\n private facetPredicate(f: FacetSpec, value: string | string[]): { sql: string; params: unknown[] } {\n const col = f.column ?? f.key\n const vals = (Array.isArray(value) ? value : [value]).filter((v) => v != null && v !== '')\n if (vals.length === 0) return { sql: '1=1', params: [] } // empty selection ⇒ no constraint\n // One value keeps today's `= ?` (byte-identical SQL); several become an OR via\n // `IN (?, ?, …)`. `cmp` wraps whichever column/JSON expression each source needs.\n const ph = vals.map(() => '?').join(', ')\n const cmp = (expr: string) => (vals.length === 1 ? `${expr} = ?` : `${expr} IN (${ph})`)\n if (f.source === 'session') {\n return f.multi\n ? { sql: `EXISTS (SELECT 1 FROM json_each(s.${col}) je WHERE ${cmp('je.value')})`, params: vals }\n : { sql: cmp(`s.${col}`), params: vals }\n }\n if (f.source === 'annotation') {\n return f.multi\n ? {\n sql: `EXISTS (SELECT 1 FROM annotations a, json_each(a.value) je\n WHERE a.session_id = s.id AND a.key = ? AND ${cmp('je.value')})`,\n params: [f.key, ...vals],\n }\n : {\n sql: `EXISTS (SELECT 1 FROM annotations a\n WHERE a.session_id = s.id AND a.key = ? AND ${cmp(\"json_extract(a.value,'$')\")})`,\n params: [f.key, ...vals],\n }\n }\n if (f.source === 'block') {\n // \"sessions with a block labeled <any selected value>\" — session-scoped EXISTS, like model/skill.\n return {\n sql: `EXISTS (SELECT 1 FROM block_annotations ba\n WHERE ba.session_id = s.id AND ba.key = ? AND ${cmp(\"json_extract(ba.value,'$')\")})`,\n params: [f.key, ...vals],\n }\n }\n const table = f.source === 'usage' ? 'usage_facts' : 'tool_calls'\n const base = f.base ? `${f.base} AND ` : ''\n return {\n sql: `EXISTS (SELECT 1 FROM ${table} c WHERE c.session_id = s.id AND ${base}${cmp(`c.${col}`)})`,\n params: vals,\n }\n }\n\n /**\n * Correlated subquery (alias `s`) yielding a session's DISTINCT values for a\n * facet as one alpha-sorted, \", \"-joined string — the composite label for the\n * success-rate breakdown; empty set → NULL. Mirrors facetPredicate's source\n * switch (identifiers/base are registry-defined and trusted, values are data).\n */\n private comboExpr(f: FacetSpec): { sql: string; params: unknown[] } {\n const col = f.column ?? f.key\n const cat = (valExpr: string, from: string, where: string, params: unknown[]) => ({\n sql: `(SELECT group_concat(v, ', ') FROM\n (SELECT DISTINCT ${valExpr} AS v FROM ${from}\n WHERE ${where} AND ${valExpr} IS NOT NULL ORDER BY v))`,\n params,\n })\n if (f.source === 'session') {\n return f.multi\n ? cat('je.value', `json_each(s.${col}) je`, '1=1', [])\n : { sql: `s.${col}`, params: [] }\n }\n if (f.source === 'annotation') {\n return f.multi\n ? cat('je.value', 'annotations a, json_each(a.value) je', 'a.session_id = s.id AND a.key = ?', [f.key])\n : { sql: `(SELECT json_extract(a.value,'$') FROM annotations a WHERE a.session_id = s.id AND a.key = ? LIMIT 1)`, params: [f.key] }\n }\n if (f.source === 'block') {\n return cat(`json_extract(ba.value,'$')`, 'block_annotations ba', 'ba.session_id = s.id AND ba.key = ?', [f.key])\n }\n const table = f.source === 'usage' ? 'usage_facts' : 'tool_calls'\n const base = f.base ? `${f.base} AND ` : ''\n return cat(`c.${col}`, `${table} c`, `${base}c.session_id = s.id`, [])\n }\n\n // ---- measures ------------------------------------------------------------\n\n /** Persist measures (intrinsic + processor-declared) for the dashboard. */\n registerMeasures(producer: string, specs: MeasureSpec[]) {\n const ins = this.db.prepare(\n 'INSERT OR REPLACE INTO measures (key, label, source, expr, agg, base, format, producer) VALUES (?,?,?,?,?,?,?,?)',\n )\n const tx = this.db.transaction(() => {\n for (const m of specs) {\n ins.run(m.key, m.label ?? m.key, m.source, m.expr, m.agg, m.base ?? null, m.format ?? null, producer)\n }\n })\n tx()\n }\n\n measureList(): MeasureSpec[] {\n const rows = this.db.prepare('SELECT * FROM measures ORDER BY key').all() as Array<Record<string, any>>\n return rows.map(rowToMeasure)\n }\n\n measure(key: string): MeasureSpec | undefined {\n const row = this.db.prepare('SELECT * FROM measures WHERE key = ?').get(key) as Record<string, any> | undefined\n return row ? rowToMeasure(row) : undefined\n }\n\n /**\n * The breakdown engine: aggregate a measure, optionally grouped by a facet,\n * with session-scoped filters. The grain guard keeps SUM/AVG honest — a facet\n * is valid here only at the measure's grain or session-grain (the common\n * ancestor). Finer / sibling facets need the pre-reduction (cohort) path, not\n * built yet, and return an error rather than a silently double-counted number.\n */\n breakdown(\n measureKey: string,\n byFacetKey?: string,\n filters?: Record<string, string>,\n window?: { from?: string; to?: string },\n toolNames?: string[],\n ): { rows: Array<{ bucket: string | null; value: number }>; total: number } | { error: string } {\n const m = this.measure(measureKey)\n if (!m) return { error: 'unknown measure' }\n const gm = grainOf(m.source)\n\n const from =\n gm === 'session'\n ? 'FROM sessions s'\n : gm === 'usage'\n ? 'FROM usage_facts u JOIN sessions s ON s.id = u.session_id'\n : 'FROM tool_calls t JOIN sessions s ON s.id = t.session_id'\n\n const where: string[] = []\n const params: unknown[] = []\n if (m.base) where.push(m.base)\n // Window the population to the dashboard's selected range (both bounds or\n // neither); every grain's FROM joins sessions s, so s.started_at is in scope.\n if (window?.from && window?.to) {\n where.push('s.started_at >= ? AND s.started_at < ?')\n params.push(window.from, window.to)\n }\n // Row-level tool-name scope — only sound for tool-call-grain measures (the FROM\n // is `tool_calls t`). Lets the Ops \"Errors by category\" widget show one tool's\n // errors, matching the error-rate chart's tool filter.\n const toolVals = (toolNames ?? []).filter(Boolean)\n if (toolVals.length && gm !== 'session' && gm !== 'usage') {\n where.push(`t.name IN (${toolVals.map(() => '?').join(', ')})`)\n params.push(...toolVals)\n }\n for (const [k, v] of Object.entries(filters ?? {})) {\n if (!v) continue\n const spec = this.facet(k)\n if (!spec) continue\n const p = this.facetPredicate(spec, v)\n where.push(p.sql)\n params.push(...p.params)\n }\n\n let groupExpr: string | null = null\n let facetJoin = ''\n if (byFacetKey) {\n const f = this.facet(byFacetKey)\n if (!f) return { error: 'unknown facet' }\n const gf = grainOf(f.source)\n if (!facetGroupCompatible(gf, gm)) return { error: 'incompatible grain' }\n const fg = facetGroupExpr(f, gm)\n groupExpr = fg.expr\n facetJoin = fg.join\n if (fg.where) where.push(fg.where)\n }\n\n const agg = aggExpr(m)\n const whereSql = where.length ? 'WHERE ' + where.join(' AND ') : ''\n\n if (groupExpr) {\n const sql = `SELECT ${groupExpr} AS bucket, ${agg} AS value ${from} ${facetJoin} ${whereSql}\n GROUP BY bucket ORDER BY value DESC LIMIT 50`\n const rows = this.db.prepare(sql).all(...params) as Array<{ bucket: string | null; value: number }>\n return { rows, total: rows.reduce((a, r) => a + (r.value ?? 0), 0) }\n }\n const sql = `SELECT ${agg} AS value ${from} ${whereSql}`\n const r = this.db.prepare(sql).get(...params) as { value: number } | undefined\n return { rows: [{ bucket: null, value: r?.value ?? 0 }], total: r?.value ?? 0 }\n }\n\n /**\n * Every failed tool call of one error category — the occurrence list behind the\n * \"Errors by category\" drill-down. Newest session first; `idx` is the tool call's\n * position in its session, which the transcript anchors as `txerr-<idx>` so a row\n * deep-links to that exact error block. Windowed like breakdown. Capped at 50; the\n * widget shows the true total (the bar count) with a \"+N more\" note past the cap.\n */\n errorOccurrences(category: string, window?: { from?: string; to?: string }, toolNames?: string[]): ErrorOccurrence[] {\n const where = ['t.error_category = ?']\n const params: unknown[] = [category]\n if (window?.from && window?.to) {\n where.push('s.started_at >= ? AND s.started_at < ?')\n params.push(window.from, window.to)\n }\n // Row-level tool scope, mirroring the widget's tool filter (Bash's timeouts, …).\n const toolVals = (toolNames ?? []).filter(Boolean)\n if (toolVals.length) {\n where.push(`t.name IN (${toolVals.map(() => '?').join(', ')})`)\n params.push(...toolVals)\n }\n const sql = `SELECT t.session_id AS sessionId, ${titleExpr('s')} AS title, t.idx AS idx,\n t.name AS name, t.action AS action, t.command AS command,\n t.target_path AS targetPath, t.error_message AS message,\n t.ts AS ts, s.started_at AS startedAt\n FROM tool_calls t JOIN sessions s ON s.id = t.session_id\n WHERE ${where.join(' AND ')}\n ORDER BY s.started_at DESC, t.idx ASC\n LIMIT 50`\n return this.db.prepare(sql).all(...params) as ErrorOccurrence[]\n }\n\n /** Filtered session list. Filter VALUES are bound params; keys are hardcoded. */\n sessionList(f: SessionFilter): SessionListItem[] {\n const scalar = (key: string) =>\n `(SELECT json_extract(value,'$') FROM annotations WHERE session_id=s.id AND key='${key}')`\n const clauses: string[] = []\n const params: unknown[] = []\n // Generic facet filters: every registered facet compiles to a session-scoped\n // predicate via one compiler. Unknown keys are ignored (only the registry\n // produces SQL), so the API can pass query params through blindly.\n for (const [key, value] of Object.entries(f.facets ?? {})) {\n if (!value) continue\n const spec = this.facet(key)\n if (!spec) continue\n const p = this.facetPredicate(spec, value)\n clauses.push(p.sql)\n params.push(...p.params)\n }\n if (f.q) {\n // Search title + intent + the decisions list (matched against its raw JSON\n // text, which is enough to surface a decision by any word it contains).\n clauses.push(\n `(${scalar('title')} LIKE ? OR s.title LIKE ? OR ${scalar('intent_summary')} LIKE ?\n OR EXISTS (SELECT 1 FROM annotations WHERE session_id=s.id AND key='decisions' AND value LIKE ?))`,\n )\n params.push(`%${f.q}%`, `%${f.q}%`, `%${f.q}%`, `%${f.q}%`)\n }\n if (f.artifact || f.artifactKind) {\n const conds: string[] = []\n if (f.artifactKind) {\n conds.push('a3.kind = ?')\n params.push(f.artifactKind)\n }\n if (f.artifact) {\n const { sql: artSql, params: artParams } = this.artifactSearchCond(f.artifact, 'a3')\n conds.push(artSql)\n params.push(...artParams)\n }\n clauses.push(\n `EXISTS (SELECT 1 FROM session_artifacts sa3 JOIN artifacts a3 ON a3.id = sa3.artifact_id\n WHERE sa3.session_id = s.id AND ${conds.join(' AND ')})`,\n )\n }\n // Window on session start (inclusive lower, exclusive upper).\n if (f.from) {\n clauses.push('s.started_at >= ?')\n params.push(f.from)\n }\n if (f.to) {\n clauses.push('s.started_at < ?')\n params.push(f.to)\n }\n // Outcome-type filter (OR): session produced ANY of the given outcome types.\n const outcomeTypes = (f.outcomeTypes ?? []).filter(Boolean)\n if (outcomeTypes.length) {\n clauses.push(\n `EXISTS (SELECT 1 FROM outcomes o WHERE o.session_id = s.id AND o.type IN (${outcomeTypes\n .map(() => '?')\n .join(',')}))`,\n )\n params.push(...outcomeTypes)\n }\n const where = clauses.length ? 'WHERE ' + clauses.join(' AND ') : ''\n const limit = Math.min(Math.max(1, f.limit ?? 200), 1000)\n\n const rows = this.db\n .prepare(\n `SELECT s.id AS id, COALESCE(${titleExpr('s')}, '(untitled)') AS title, s.started_at AS startedAt,\n s.cost_usd AS costUsd, s.models AS modelsJson,\n ${scalar('complexity')} AS complexity,\n (SELECT json_group_array(v) FROM (SELECT DISTINCT json_extract(value,'$') AS v FROM block_annotations WHERE session_id=s.id AND key='use_case' ORDER BY v)) AS useCaseJson,\n ${scalar('intent_summary')} AS intent,\n (SELECT json_group_array(t) FROM (SELECT DISTINCT type AS t FROM outcomes WHERE session_id=s.id ORDER BY t)) AS outcomesJson\n FROM sessions s ${where} ORDER BY s.started_at DESC LIMIT ${limit}`,\n )\n .all(...params) as Array<Record<string, any>>\n\n return rows.map((r) => ({\n id: r.id,\n title: r.title,\n startedAt: r.startedAt,\n costUsd: r.costUsd ?? 0,\n models: safeJson(r.modelsJson, []),\n complexity: r.complexity ?? null,\n useCase: safeJson(r.useCaseJson, []),\n intent: r.intent ?? null,\n outcomes: safeJson(r.outcomesJson, []),\n }))\n }\n\n /** Full detail for one session, including a viewer-ready transcript from the blob. */\n /** Per-block labels (use_case / PR / feature) for the transcript filter bar. */\n private blockLabels(\n id: string,\n ): Map<number, { useCase?: string | null; pr?: { ident: string; title?: string } | null; feature?: { id: string; title?: string } | null }> {\n type Lbl = { useCase?: string | null; pr?: { ident: string; title?: string } | null; feature?: { id: string; title?: string } | null }\n const m = new Map<number, Lbl>()\n const ensure = (idx: number): Lbl => {\n let e = m.get(idx)\n if (!e) { e = {}; m.set(idx, e) }\n return e\n }\n const ucRows = this.db\n .prepare(\"SELECT block_idx AS idx, json_extract(value,'$') AS v FROM block_annotations WHERE session_id = ? AND key = 'use_case'\")\n .all(id) as Array<{ idx: number; v: string }>\n for (const r of ucRows) ensure(r.idx).useCase = r.v\n const artRows = this.db\n .prepare(\n `SELECT ba.block_idx AS idx, a.id AS aid, a.ident, a.title, a.kind\n FROM block_artifacts ba JOIN artifacts a ON a.id = ba.artifact_id\n WHERE ba.session_id = ? AND a.kind IN ('pr','feature')`,\n )\n .all(id) as Array<{ idx: number; aid: string; ident: string | null; title: string | null; kind: string }>\n for (const r of artRows) {\n const e = ensure(r.idx)\n if (r.kind === 'pr') e.pr = { ident: r.ident ?? '?', title: r.title ?? undefined }\n else e.feature = { id: r.aid, title: r.title ?? undefined }\n }\n return m\n }\n\n sessionDetail(id: string): SessionDetail | null {\n const s = this.db\n .prepare(\n `SELECT id, ${titleExpr('sessions')} AS title, source, provider, repo, branch, started_at AS startedAt, ended_at AS endedAt,\n n_turns AS nTurns, n_tool_calls AS nToolCalls, models AS modelsJson, cost_usd AS costUsd,\n tok_input AS tokInput, tok_output AS tokOutput, tok_cache_create AS tokCacheCreate, tok_cache_read AS tokCacheRead\n FROM sessions WHERE id = ?`,\n )\n .get(id) as Record<string, any> | undefined\n if (!s) return null\n\n const annRows = this.db.prepare('SELECT key, value FROM annotations WHERE session_id = ?').all(id) as Array<{\n key: string\n value: string\n }>\n const annotations: Record<string, unknown> = {}\n for (const a of annRows) annotations[a.key] = safeJson(a.value, null)\n\n const outcomes = this.db\n .prepare('SELECT type, artifact_id AS artifactId FROM outcomes WHERE session_id = ?')\n .all(id) as Array<{ type: string; artifactId: string | null }>\n\n // One row per artifact: a session can link the same PR under multiple roles\n // (e.g. `created` by outcomes-git + `edited` by pr-content-match for its\n // attribution %), so pick the strongest/most-explicit link for display.\n const artifacts = this.db\n .prepare(\n `SELECT id, kind, title, ident, status, repo, externalId, role, source, confidence FROM (\n SELECT a.id, a.kind, a.title, a.ident, a.status, a.repo, a.external_id AS externalId, sa.role, sa.source,\n MAX(CASE WHEN sa.producer = 'pr-content-match' THEN sa.confidence END) OVER (PARTITION BY a.id) AS confidence,\n ROW_NUMBER() OVER (PARTITION BY a.id ORDER BY\n CASE sa.role WHEN 'created' THEN 0 WHEN 'reviewed' THEN 1 WHEN 'edited' THEN 2 ELSE 3 END,\n CASE COALESCE(sa.source,'') WHEN 'explicit' THEN 0 WHEN 'user' THEN 1 ELSE 2 END) AS rn\n FROM session_artifacts sa JOIN artifacts a ON a.id = sa.artifact_id WHERE sa.session_id = ?\n ) WHERE rn = 1`,\n )\n .all(id) as Array<Record<string, any>>\n\n let transcript: Transcript = { turns: [], subagents: [], blocks: [] }\n const blob = this.db.prepare('SELECT gz FROM session_blobs WHERE id = ?').get(id) as { gz: Buffer } | undefined\n if (blob?.gz) {\n try {\n transcript = buildTranscript(JSON.parse(gunzipSync(blob.gz).toString('utf8')) as Session)\n } catch {\n /* leave empty */\n }\n }\n // Attach per-block labels (use_case / PR / feature) the filter bar groups by.\n if (transcript.blocks.length) {\n const labels = this.blockLabels(id)\n transcript.blocks = transcript.blocks.map((b) => ({ idx: b.idx, ...(labels.get(b.idx) ?? {}) }))\n }\n\n return {\n session: {\n id: s.id,\n title: s.title,\n source: s.source,\n provider: s.provider,\n repo: s.repo,\n branch: s.branch,\n startedAt: s.startedAt,\n endedAt: s.endedAt,\n nTurns: s.nTurns,\n nToolCalls: s.nToolCalls,\n models: safeJson(s.modelsJson, []),\n costUsd: s.costUsd ?? 0,\n tokens: {\n input: s.tokInput ?? 0,\n output: s.tokOutput ?? 0,\n cacheCreate: s.tokCacheCreate ?? 0,\n cacheRead: s.tokCacheRead ?? 0,\n },\n },\n annotations,\n outcomes,\n artifacts,\n facets: this.facetValues(id),\n transcript,\n }\n }\n\n /**\n * The session's value for every facet flagged for the `detail` role — the\n * registry-driven metadata list the drawer renders. Keeps the drawer from\n * hardcoding which dimensions exist: a new processor facet with a `detail`\n * role appears here with no store or client edits. Ordered by registration\n * (intrinsic first, then processors) rather than alphabetically.\n */\n facetValues(id: string): FacetValue[] {\n const facets = (this.db.prepare('SELECT * FROM facets ORDER BY rowid').all() as Array<Record<string, any>>)\n .map(rowToFacet)\n .filter((f) => (f.roles ?? []).includes('detail'))\n return facets.map((f) => ({ key: f.key, label: f.label ?? f.key, type: f.type, value: this.facetValueFor(f, id) }))\n }\n\n /** Resolve one facet's value(s) for a session, branching on (source, multi) like facetPredicate. */\n private facetValueFor(f: FacetSpec, id: string): string | string[] | null {\n const col = f.column ?? f.key\n if (f.source === 'session') {\n if (f.multi) {\n const rows = this.db\n .prepare(`SELECT je.value AS v FROM sessions s, json_each(s.${col}) je WHERE s.id = ?`)\n .all(id) as Array<{ v: unknown }>\n return rows.map((r) => String(r.v))\n }\n const row = this.db.prepare(`SELECT ${col} AS v FROM sessions WHERE id = ?`).get(id) as { v: unknown } | undefined\n return row?.v == null || row.v === '' ? null : String(row.v)\n }\n if (f.source === 'annotation') {\n const row = this.db.prepare('SELECT value FROM annotations WHERE session_id = ? AND key = ?').get(id, f.key) as\n | { value: string }\n | undefined\n const parsed = row ? safeJson<unknown>(row.value, null) : null\n if (f.multi) return Array.isArray(parsed) ? parsed.map(String) : []\n return parsed == null || parsed === '' ? null : String(parsed)\n }\n if (f.source === 'block') {\n // the distinct block labels this session exhibits (union rollup, e.g. use_case)\n const rows = this.db\n .prepare(\n `SELECT DISTINCT json_extract(value,'$') AS v FROM block_annotations WHERE session_id = ? AND key = ? ORDER BY v`,\n )\n .all(id, f.key) as Array<{ v: unknown }>\n return rows.map((r) => String(r.v))\n }\n // usage / tool-call child grain: the distinct values this session exhibits.\n const table = f.source === 'usage' ? 'usage_facts' : 'tool_calls'\n const base = f.base ? `${f.base} AND ` : ''\n const rows = this.db\n .prepare(`SELECT DISTINCT ${col} AS v FROM ${table} WHERE session_id = ? AND ${base}${col} IS NOT NULL AND ${col} <> '' ORDER BY ${col}`)\n .all(id) as Array<{ v: unknown }>\n return rows.map((r) => String(r.v))\n }\n\n /** Gunzip + parse a session's stored blob (the full normalized Session), or null. */\n private loadSession(id: string): Session | null {\n const blob = this.db.prepare('SELECT gz FROM session_blobs WHERE id = ?').get(id) as { gz: Buffer } | undefined\n if (!blob?.gz) return null\n try {\n return JSON.parse(gunzipSync(blob.gz).toString('utf8')) as Session\n } catch {\n return null\n }\n }\n\n /**\n * The session's successful file edits as a flat, CHRONOLOGICAL list — the\n * Files-changed view. Each carries its raw before/after (Edit), full content\n * (Write), or hunks (MultiEdit), plus the transcript turn it happened in and\n * the preceding (non-synthetic) user turn, so the UI can group by file or by\n * prompt and link each change to its intent. Rejected / not-yet-read edits are\n * excluded (they changed nothing). Reconstructs from the blob at read time.\n */\n fileChanges(id: string): FileEdit[] {\n const session = this.loadSession(id)\n if (!session) return []\n const { toolTurn } = buildTranscriptCore(session)\n const out: FileEdit[] = []\n for (const tc of session.toolCalls) {\n if (tc.action !== 'file_write' || !tc.result.ok) continue\n const ref = toolTurn.get(tc.id) ?? { turn: -1, userTurn: -1 }\n // Codex's `apply_patch` stores the raw V4A patch TEXT (a string) and can touch\n // several files in one call. Expand it to one FileEdit per file; the object-shaped\n // path below (Claude Code, OpenCode) handles `{content}` / `{old_string,…}` inputs.\n if (typeof tc.input === 'string') {\n for (const fe of parseApplyPatch(tc.input)) {\n out.push({ ...fe, ts: tc.ts, turn: ref.turn, userTurn: ref.userTurn })\n }\n continue\n }\n const path = tc.target.paths?.[0]\n if (!path) continue\n const input = (tc.input ?? {}) as Record<string, unknown>\n let op: FileEdit['op']\n let hunks: Array<{ del: string; ins: string }>\n // Distinguish write/multiedit/edit by input shape, not the raw tool name —\n // names are case- and vendor-specific (`Write` vs `write`). Field spellings\n // also differ (Claude Code: old_string/new_string; OpenCode: oldString/newString).\n if (Array.isArray(input.edits)) {\n op = 'multiedit'\n hunks = (input.edits as Array<Record<string, unknown>>).map((e) => ({\n del: clip(String(e.old_string ?? e.oldString ?? ''), 4000),\n ins: clip(String(e.new_string ?? e.newString ?? ''), 4000),\n }))\n } else if (input.content != null) {\n op = 'write'\n // Cap is generous because consecutive writes are diffed against each\n // other client-side; too small a window hides changes near the file end.\n hunks = [{ del: '', ins: clip(String(input.content ?? ''), 16000) }]\n } else {\n op = 'edit'\n hunks = [\n {\n del: clip(String(input.old_string ?? input.oldString ?? ''), 4000),\n ins: clip(String(input.new_string ?? input.newString ?? ''), 4000),\n },\n ]\n }\n out.push({ path, op, hunks, ts: tc.ts, turn: ref.turn, userTurn: ref.userTurn })\n }\n return out\n }\n\n /**\n * Shippable artifacts (PRs + features) with session count and fully-loaded\n * cost. Cost sums the UNIQUE sessions linked to each artifact; a session\n * spanning several artifacts is counted in each, so the column can exceed\n * total spend (per-artifact attribution, by design).\n */\n artifactList(kind?: string, complexity?: string, from?: string, to?: string, shippedOnly = false): ArtifactListItem[] {\n const allowed = ['pr', 'feature', 'ticket']\n const kinds = kind && allowed.includes(kind) ? [kind] : ['pr', 'feature']\n const placeholders = kinds.map(() => '?').join(',')\n // Complexity filter (buckets → this artifact's own complexity). No-op unless a\n // single kind is requested — the bucket→value mapping differs by kind.\n const cxFilter = complexityWhere(complexity, 'a', kind)\n // Completion window: keep only artifacts completed (PR merged) in [from,to], to\n // match the cost-per-artifact KPI's basis — the per-artifact cost stays all-time;\n // the window selects which artifacts count. No window (all-time) keeps every row,\n // including still-open PRs (the Artifacts tab relies on that).\n const range = from && to ? 'AND a.completed_at >= ? AND a.completed_at < ?' : ''\n const winParams = from && to ? [from, to] : []\n // Shipped/merged-only (the cost-breakdown treemap): require a completion date so\n // only shipped artifacts show — the same basis as costPerArtifact (which counts\n // every completed artifact you contributed to, at full cost), so the tiles\n // reconcile with the KPI. The HAVING clause below already requires a session link,\n // so a merged PR you only reviewed still qualifies. The Artifacts tab leaves this\n // off to list open/un-shipped rows too.\n const shippedFilter = shippedOnly ? 'AND a.completed_at IS NOT NULL' : ''\n const rows = this.db\n .prepare(\n `SELECT a.id, a.kind, a.title, a.ident, a.repo, a.status, a.source,\n a.external_id AS externalId, a.created_at AS createdAt, a.completed_at AS completedAt,\n a.parent_artifact_id AS parentId, a.complexity, a.complexity_basis AS complexityBasis,\n -- AI-attribution % = pr-content-match's confidence only (other producers write\n -- confidence on session_artifacts too, e.g. review links).\n MAX(CASE WHEN sa.producer = 'pr-content-match' THEN sa.confidence END) AS aiPct,\n COUNT(DISTINCT sa.session_id) AS sessions,\n COALESCE((\n SELECT SUM(cost_usd) FROM (\n -- block-attributed cost (block→artifact): blocks partition the\n -- session, so a multi-purpose session isn't charged whole (P1).\n SELECT DISTINCT u.session_id, u.idx AS uidx, u.cost_usd\n FROM block_artifacts ba\n JOIN block_usage bu ON bu.session_id = ba.session_id AND bu.block_idx = ba.block_idx\n JOIN usage_facts u ON u.session_id = bu.session_id AND u.idx = bu.usage_idx\n WHERE ba.artifact_id = a.id\n )\n ), 0) AS costUsd\n FROM artifacts a\n LEFT JOIN session_artifacts sa ON sa.artifact_id = a.id\n WHERE a.kind IN (${placeholders}) ${cxFilter} ${range} ${shippedFilter}\n GROUP BY a.id\n HAVING (COUNT(DISTINCT sa.session_id) > 0 OR COALESCE(a.source,'') = 'user')\n ORDER BY COALESCE(a.created_at, a.completed_at) DESC, costUsd DESC`,\n )\n .all(...kinds, ...winParams) as ArtifactListItem[]\n // Attach the repos each row spans and its last session time. Features use the\n // subtree union/max (an epic spans/aggregates everything under it); other\n // kinds (PRs) just carry their own repo and aren't shown a last-session time.\n const hasFeature = rows.some((r) => r.kind === 'feature')\n const repoSets = hasFeature ? this.featureRepoSets() : null\n const lastSession = hasFeature ? this.featureLastSession() : null\n for (const r of rows) {\n r.repos = r.kind === 'feature' ? (repoSets?.get(r.id) ?? []) : r.repo ? [r.repo] : []\n r.lastSessionAt = r.kind === 'feature' ? (lastSession?.get(r.id) ?? null) : null\n }\n return rows\n }\n\n /**\n * Per-feature last session time: the most recent start of any session linked to\n * the feature OR any descendant (subtree max), so a parent reflects the latest\n * activity beneath it. Null when nothing under it has a dated session.\n */\n private featureLastSession(): Map<string, string | null> {\n const rows = this.db\n .prepare(\"SELECT id, parent_artifact_id AS parentId FROM artifacts WHERE kind = 'feature'\")\n .all() as Array<{ id: string; parentId: string | null }>\n const direct = new Map<string, string>()\n const dl = this.db\n .prepare(\n `SELECT sa.artifact_id AS id, MAX(s.started_at) AS last\n FROM session_artifacts sa JOIN sessions s ON s.id = sa.session_id\n JOIN artifacts a ON a.id = sa.artifact_id\n WHERE a.kind = 'feature' AND s.started_at IS NOT NULL\n GROUP BY sa.artifact_id`,\n )\n .all() as Array<{ id: string; last: string | null }>\n for (const r of dl) if (r.last) direct.set(r.id, r.last)\n\n const children = new Map<string, string[]>()\n for (const r of rows) {\n if (!r.parentId) continue\n const arr = children.get(r.parentId)\n if (arr) arr.push(r.id)\n else children.set(r.parentId, [r.id])\n }\n const memo = new Map<string, string | null>()\n const onStack = new Set<string>()\n const subtreeMax = (id: string): string | null => {\n const cached = memo.get(id)\n if (cached !== undefined) return cached\n let max = direct.get(id) ?? null\n if (!onStack.has(id)) {\n onStack.add(id)\n for (const c of children.get(id) ?? []) {\n const cm = subtreeMax(c)\n if (cm && (!max || cm > max)) max = cm // ISO timestamps compare lexically\n }\n onStack.delete(id)\n }\n memo.set(id, max)\n return max\n }\n const out = new Map<string, string | null>()\n for (const r of rows) out.set(r.id, subtreeMax(r.id))\n return out\n }\n\n /**\n * Per-feature cost rolled up over the feature hierarchy, for the hierarchical\n * cost-breakdown charts. `ownCost` is the spend attributed DIRECTLY to a feature\n * (the same block-attributed-with-whole-session-fallback cost as the artifactList\n * column); `subtreeCost` adds every descendant's own cost, so a parent epic\n * reflects the total invested beneath it (subtreeCost − Σ children.subtreeCost =\n * ownCost). All-time, not windowed: total investment per feature, matching the\n * artifactList cost semantics. Cycle-safe + memoized, mirroring featureLastSession.\n * parentId is normalized to null when it doesn't point at another feature, so the\n * client can treat such rows as roots.\n */\n featureCostTree(complexity?: string, from?: string, to?: string): Array<{\n id: string\n title: string | null\n parentId: string | null\n ownCost: number\n subtreeCost: number\n }> {\n // Complexity filter on each feature's own ordinal. Applied identically to the\n // node set and the cost query below, so survivors stay consistent; orphaned\n // children (parent filtered out) re-root and subtreeCost rolls up only the\n // surviving descendants — see the parentId/children normalization below.\n const cxNodes = complexityWhere(complexity, 'artifacts', 'feature')\n const cxCost = complexityWhere(complexity, 'a', 'feature')\n // Only SHIPPED features (completed_at set) — the treemap decomposes the\n // cost-per-shipped-feature KPI, so un-shipped features are excluded (matching\n // costPerArtifact). A window further bounds it to features shipped in [from,to];\n // per-feature cost stays all-time (the window picks which features count, not\n // which spend). No window = all shipped features.\n const win = from && to\n const rangeNodes = win ? 'AND completed_at >= ? AND completed_at < ?' : ''\n const rangeCost = win ? 'AND a.completed_at >= ? AND a.completed_at < ?' : ''\n const winParams = win ? [from, to] : []\n const rows = this.db\n .prepare(\n `SELECT id, title, parent_artifact_id AS parentId\n FROM artifacts WHERE kind = 'feature' AND completed_at IS NOT NULL ${cxNodes} ${rangeNodes}`,\n )\n .all(...winParams) as Array<{ id: string; title: string | null; parentId: string | null }>\n if (!rows.length) return []\n // Own (direct) cost per feature — identical attribution to artifactList's cost\n // column: purely block-attributed (a feature with no block links contributes zero;\n // DISTINCT so a shared usage row counts once).\n const costRows = this.db\n .prepare(\n `SELECT a.id AS id, COALESCE((\n SELECT SUM(cost_usd) FROM (\n SELECT DISTINCT u.session_id, u.idx AS uidx, u.cost_usd\n FROM block_artifacts ba\n JOIN block_usage bu ON bu.session_id = ba.session_id AND bu.block_idx = ba.block_idx\n JOIN usage_facts u ON u.session_id = bu.session_id AND u.idx = bu.usage_idx\n WHERE ba.artifact_id = a.id\n )\n ), 0) AS ownCost\n FROM artifacts a WHERE a.kind = 'feature' AND a.completed_at IS NOT NULL ${cxCost} ${rangeCost}`,\n )\n .all(...winParams) as Array<{ id: string; ownCost: number }>\n const own = new Map<string, number>()\n for (const r of costRows) own.set(r.id, r.ownCost || 0)\n\n const children = new Map<string, string[]>()\n for (const r of rows) {\n if (!r.parentId || !own.has(r.parentId)) continue\n const arr = children.get(r.parentId)\n if (arr) arr.push(r.id)\n else children.set(r.parentId, [r.id])\n }\n const memo = new Map<string, number>()\n const onStack = new Set<string>()\n const subtreeCost = (id: string): number => {\n const cached = memo.get(id)\n if (cached !== undefined) return cached\n let sum = own.get(id) ?? 0\n if (!onStack.has(id)) {\n onStack.add(id)\n for (const c of children.get(id) ?? []) sum += subtreeCost(c)\n onStack.delete(id)\n }\n memo.set(id, sum)\n return sum\n }\n return rows.map((r) => ({\n id: r.id,\n title: r.title,\n parentId: r.parentId && own.has(r.parentId) ? r.parentId : null,\n ownCost: own.get(r.id) ?? 0,\n subtreeCost: subtreeCost(r.id),\n }))\n }\n\n /**\n * Build a SQL condition + params for artifact text search that handles plain\n * terms, `#N` (PR number with hash prefix), and `repo#N` (repo + number).\n */\n private artifactSearchCond(term: string, alias: string): { sql: string; params: unknown[] } {\n const a = alias\n const base = `%${term}%`\n const parts: string[] = [`${a}.ident LIKE ?`, `${a}.title LIKE ?`, `${a}.external_id LIKE ?`, `${a}.repo LIKE ?`]\n const params: unknown[] = [base, base, base, base]\n if (term.startsWith('#')) {\n parts.push(`${a}.ident LIKE ?`)\n params.push(`%${term.slice(1)}%`)\n }\n const hashIdx = term.indexOf('#')\n if (hashIdx > 0) {\n const repoPart = term.slice(0, hashIdx)\n const numPart = term.slice(hashIdx + 1)\n if (repoPart && numPart) {\n parts.push(`(${a}.repo LIKE ? AND ${a}.ident LIKE ?)`)\n params.push(`%${repoPart}%`, `%${numPart}%`)\n }\n }\n return { sql: '(' + parts.join(' OR ') + ')', params }\n }\n\n /**\n * Typeahead suggestions for the session-list artifact search. Only artifacts\n * actually linked to a session (so a pick yields results), matched on the same\n * columns the filter uses (ident/title/external_id/repo). `value` is what to\n * put in the filter input (feature→title, pr→external_id|ident, file→path);\n * `label` is for display. Features/PRs rank above the many file rows.\n */\n suggestArtifacts(q: string, kind: string | undefined, limit = 10): Array<{ kind: string; value: string; label: string }> {\n const term = q.trim()\n if (!term) return []\n const allowed = ['file', 'pr', 'feature']\n const kindFilter = kind && allowed.includes(kind) ? 'AND a.kind = ?' : ''\n const { sql: searchSql, params } = this.artifactSearchCond(term, 'a')\n if (kindFilter) params.push(kind)\n params.push(limit)\n const rows = this.db\n .prepare(\n `SELECT a.kind AS kind, a.ident AS ident, a.title AS title, a.external_id AS externalId,\n a.repo AS repo, a.status AS status\n FROM artifacts a\n WHERE a.id IN (SELECT artifact_id FROM session_artifacts)\n AND ${searchSql}\n ${kindFilter}\n GROUP BY a.id\n ORDER BY CASE a.kind WHEN 'feature' THEN 0 WHEN 'pr' THEN 1 ELSE 2 END,\n COALESCE(a.title, a.ident)\n LIMIT ?`,\n )\n .all(...params) as Array<Record<string, any>>\n return rows\n .map((r) => {\n if (r.kind === 'pr') {\n const label = (r.repo ? r.repo + ' ' : '') + '#' + (r.ident ?? '') +\n (r.title ? ' — ' + r.title : '') + (r.status ? ' (' + r.status + ')' : '')\n return { kind: 'pr', value: String(r.externalId || r.ident || ''), label }\n }\n if (r.kind === 'feature') return { kind: 'feature', value: String(r.title ?? ''), label: String(r.title || '(untitled)') }\n return { kind: 'file', value: String(r.ident ?? ''), label: String(r.ident ?? '') }\n })\n .filter((x) => x.value)\n }\n\n // ---- feature management (dashboard writes) --------------------------------\n\n /** Create a user-authored feature (source='user' — never clobbered by analyze). */\n createFeature(title: string, parentId?: string, complexity?: number): { id: string } {\n const id = `feature:user:${randomUUID().slice(0, 8)}`\n this.db\n .prepare(\n `INSERT INTO artifacts (id, kind, title, source, created_at, parent_artifact_id, complexity, complexity_basis)\n VALUES (?, 'feature', ?, 'user', ?, ?, ?, ?)`,\n )\n .run(id, title, new Date().toISOString(), parentId ?? null, complexity ?? null, complexity != null ? 'user_tagged' : null)\n return { id }\n }\n\n /** Mark complete/reopen, rename, reparent, or set complexity of a feature. */\n updateFeature(id: string, patch: { completed?: boolean; parentId?: string | null; title?: string; complexity?: number | null }): boolean {\n const exists = this.db.prepare(\"SELECT 1 FROM artifacts WHERE id = ? AND kind = 'feature'\").get(id)\n if (!exists) return false\n if (patch.completed !== undefined) {\n if (patch.completed) {\n this.db.prepare(\"UPDATE artifacts SET completed_at = ?, status = 'shipped' WHERE id = ?\").run(new Date().toISOString(), id)\n } else {\n this.db.prepare('UPDATE artifacts SET completed_at = NULL, status = NULL WHERE id = ?').run(id)\n }\n }\n if (patch.parentId !== undefined && patch.parentId !== id) {\n this.db.prepare('UPDATE artifacts SET parent_artifact_id = ? WHERE id = ?').run(patch.parentId ?? null, id)\n }\n if (patch.title !== undefined && patch.title.trim()) {\n this.db.prepare('UPDATE artifacts SET title = ? WHERE id = ?').run(patch.title.trim(), id)\n }\n if (patch.complexity !== undefined) {\n this.db.prepare('UPDATE artifacts SET complexity = ?, complexity_basis = ? WHERE id = ?').run(\n patch.complexity, patch.complexity !== null ? 'user_tagged' : null, id,\n )\n }\n return true\n }\n\n /** Delete a feature; promote its children to its parent and remove its links. */\n deleteFeature(id: string): boolean {\n const row = this.db.prepare(\"SELECT parent_artifact_id AS p FROM artifacts WHERE id = ? AND kind = 'feature'\").get(id) as\n | { p: string | null }\n | undefined\n if (!row) return false\n this.db.transaction(() => {\n this.db.prepare('UPDATE artifacts SET parent_artifact_id = ? WHERE parent_artifact_id = ?').run(row.p ?? null, id)\n this.db.prepare('DELETE FROM session_artifacts WHERE artifact_id = ?').run(id)\n this.db.prepare('DELETE FROM artifact_links WHERE from_id = ? OR to_id = ?').run(id, id)\n this.db.prepare('DELETE FROM artifacts WHERE id = ?').run(id)\n })()\n return true\n }\n\n /**\n * Delete machine-derived artifacts no longer referenced by any session or\n * link (e.g. PRs whose false-positive links were removed on re-derivation).\n * Never touches user-authored artifacts.\n */\n /**\n * Delete sessions whose parse_version is below the current version for their\n * source — these are sessions the parser now returns null for (e.g. synthetic-only).\n */\n pruneStaleSessionsByVersion(versionBySource: Map<string, number>): number {\n let total = 0\n for (const [source, version] of versionBySource) {\n const r = this.db\n .prepare('DELETE FROM sessions WHERE source = ? AND parse_version < ?')\n .run(source, version)\n total += r.changes\n }\n return total\n }\n\n pruneOrphanArtifacts(): number {\n const r = this.db\n .prepare(\n `DELETE FROM artifacts\n WHERE COALESCE(source,'') <> 'user'\n AND id NOT IN (SELECT artifact_id FROM session_artifacts)\n AND id NOT IN (SELECT from_id FROM artifact_links)\n AND id NOT IN (SELECT to_id FROM artifact_links)`,\n )\n .run()\n return r.changes\n }\n\n // ---- session-link management (dashboard writes) ----------------------------\n\n /** Link an existing artifact to a session (user-authored, never overwritten by processors). Clears any prior rejection tombstone. */\n addSessionLink(sessionId: string, artifactId: string, role: SessionArtifactRole = 'contributed'): boolean {\n const session = this.db.prepare('SELECT 1 FROM sessions WHERE id = ?').get(sessionId)\n const artifact = this.db.prepare('SELECT 1 FROM artifacts WHERE id = ?').get(artifactId)\n if (!session || !artifact) return false\n this.db.transaction(() => {\n this.linkUserArtifact(sessionId, artifactId, role)\n this.db.prepare('DELETE FROM user_link_overrides WHERE session_id = ? AND artifact_id = ?').run(sessionId, artifactId)\n })()\n return true\n }\n\n /** Reject a session→artifact link: delete existing rows and insert a tombstone so re-enrichment won't recreate it. */\n rejectSessionLink(sessionId: string, artifactId: string): boolean {\n const session = this.db.prepare('SELECT 1 FROM sessions WHERE id = ?').get(sessionId)\n if (!session) return false\n this.db.transaction(() => {\n this.db.prepare('DELETE FROM session_artifacts WHERE session_id = ? AND artifact_id = ?').run(sessionId, artifactId)\n this.db.prepare('DELETE FROM block_artifacts WHERE session_id = ? AND artifact_id = ?').run(sessionId, artifactId)\n this.db\n .prepare(\n `INSERT OR REPLACE INTO user_link_overrides (session_id, artifact_id, action, created_at)\n VALUES (?, ?, 'reject', ?)`,\n )\n .run(sessionId, artifactId, new Date().toISOString())\n this.invalidateSessionProcessors(sessionId)\n })()\n return true\n }\n\n /**\n * Mark every processor run for a session stale so the next analyze re-runs them.\n *\n * Called on any user link/unlink. The user-linked set feeds enrichment, and\n * unlink deletes block_artifacts across producers, so the deterministic\n * processors (outcomes-git) must re-derive too — enrich-only invalidation\n * would leave their wiped rows unregenerated. We flag rather than delete so\n * the row's cost_usd/tokens survive; persistResult resets the flag on the\n * next successful run. Cost: at most one extra analyze per explicit user\n * action (not on every analyze).\n */\n private invalidateSessionProcessors(sessionId: string): void {\n this.db.prepare('UPDATE processor_runs SET invalidated = 1 WHERE session_id = ?').run(sessionId)\n }\n\n /**\n * The single writer for user-created session links. Every dashboard link path\n * funnels through here so the write and its cache invalidation can never drift\n * apart (that drift is how add-pr/create-feature previously skipped it). Call\n * within the caller's own transaction so the link and invalidation commit atomically.\n */\n private linkUserArtifact(sessionId: string, artifactId: string, role: SessionArtifactRole = 'contributed'): void {\n this.db\n .prepare(\n `INSERT OR REPLACE INTO session_artifacts (session_id, artifact_id, role, source, confidence, producer)\n VALUES (?, ?, ?, 'user', 1.0, 'dashboard')`,\n )\n .run(sessionId, artifactId, role)\n this.invalidateSessionProcessors(sessionId)\n }\n\n /** Titles of features the user rejected for this session (for LLM prompt context). */\n rejectedFeatureTitles(sessionId: string): string[] {\n const rows = this.db\n .prepare(\n `SELECT a.title FROM user_link_overrides o\n JOIN artifacts a ON a.id = o.artifact_id\n WHERE o.session_id = ? AND o.action = 'reject' AND a.kind = 'feature' AND a.title IS NOT NULL`,\n )\n .all(sessionId) as Array<{ title: string }>\n return rows.map((r) => r.title)\n }\n\n /** Blocks already attributed to PRs for a session (deterministic, from outcomes-git). */\n prBlockAttributions(sessionId: string): Array<{ blockIdx: number; artifactId: string; title: string | null }> {\n return this.db\n .prepare(\n `SELECT ba.block_idx AS blockIdx, ba.artifact_id AS artifactId, a.title\n FROM block_artifacts ba\n JOIN artifacts a ON a.id = ba.artifact_id\n WHERE ba.session_id = ? AND a.kind = 'pr' AND COALESCE(ba.role,'') <> 'reviewed' AND ba.producer <> 'enrich-session'`,\n )\n .all(sessionId) as Array<{ blockIdx: number; artifactId: string; title: string | null }>\n }\n\n /** All user-linked PRs/features for a session, with a flag indicating deterministic block ownership. */\n userLinkedArtifactsAll(sessionId: string): Array<{ artifactId: string; kind: 'pr' | 'feature'; title: string | null; ident: string | null; hasNonEnrichBlocks: boolean }> {\n return (this.db\n .prepare(\n `SELECT a.id AS artifactId, a.kind, a.title, a.ident,\n EXISTS(SELECT 1 FROM block_artifacts ba\n WHERE ba.session_id = sa.session_id\n AND ba.artifact_id = sa.artifact_id\n AND ba.producer <> 'enrich-session') AS hasNonEnrichBlocks\n FROM session_artifacts sa\n JOIN artifacts a ON a.id = sa.artifact_id\n WHERE sa.session_id = ? AND sa.source = 'user' AND a.kind IN ('pr', 'feature')`,\n )\n .all(sessionId) as Array<{ artifactId: string; kind: 'pr' | 'feature'; title: string | null; ident: string | null; hasNonEnrichBlocks: number }>)\n .map((r) => ({ ...r, hasNonEnrichBlocks: r.hasNonEnrichBlocks === 1 }))\n }\n\n /** Create a new feature and link it to a session in one transaction. */\n createAndLinkFeature(sessionId: string, title: string, parentId?: string): { id: string } | null {\n const session = this.db.prepare('SELECT 1 FROM sessions WHERE id = ?').get(sessionId)\n if (!session) return null\n let id: string | undefined\n this.db.transaction(() => {\n id = this.createFeature(title, parentId).id\n this.linkUserArtifact(sessionId, id)\n })()\n return { id: id! }\n }\n\n /** Upsert a PR artifact and link it to a session. */\n upsertAndLinkPr(\n sessionId: string,\n repo: string,\n prNumber: string,\n meta?: { title?: string; status?: string; externalId?: string },\n ): { id: string } | null {\n const session = this.db.prepare('SELECT 1 FROM sessions WHERE id = ?').get(sessionId)\n if (!session) return null\n const id = `pr:${repo}:${prNumber}`\n this.db.transaction(() => {\n this.db\n .prepare(\n `INSERT INTO artifacts (id, kind, repo, ident, external_id, source, title, status, producer)\n VALUES (?, 'pr', ?, ?, ?, 'user', ?, ?, 'dashboard')\n ON CONFLICT(id) DO UPDATE SET\n title = COALESCE(excluded.title, artifacts.title),\n status = COALESCE(excluded.status, artifacts.status),\n external_id = COALESCE(excluded.external_id, artifacts.external_id)`,\n )\n .run(id, repo, prNumber, meta?.externalId ?? null, meta?.title ?? null, meta?.status ?? null)\n this.linkUserArtifact(sessionId, id)\n })()\n return { id }\n }\n\n /** Typeahead for linkable artifacts (excludes those already linked to the session). */\n suggestLinkableArtifacts(\n sessionId: string,\n q: string,\n kind?: string,\n limit = 10,\n ): Array<{ id: string; kind: string; label: string }> {\n const term = q.trim()\n if (!term) return []\n const { sql: searchSql, params } = this.artifactSearchCond(term, 'a')\n if (kind) params.push(kind)\n params.push(sessionId)\n params.push(limit)\n const rows = this.db\n .prepare(\n `SELECT a.id, a.kind, a.ident, a.title, a.repo, a.status\n FROM artifacts a\n WHERE ${searchSql}\n ${kind ? 'AND a.kind = ?' : ''}\n AND a.id NOT IN (SELECT artifact_id FROM session_artifacts WHERE session_id = ?)\n ORDER BY CASE a.kind WHEN 'feature' THEN 0 WHEN 'pr' THEN 1 WHEN 'commit' THEN 2 ELSE 3 END,\n COALESCE(a.title, a.ident)\n LIMIT ?`,\n )\n .all(...params) as Array<Record<string, any>>\n return rows.map((r) => ({\n id: r.id as string,\n kind: r.kind as string,\n label:\n r.kind === 'pr'\n ? `${r.repo || ''} #${r.ident || ''}${r.title ? ' — ' + r.title : ''}${r.status ? ' (' + r.status + ')' : ''}`\n : String(r.title || r.ident || r.id),\n }))\n }\n\n close() {\n this.db.close()\n }\n}\n\nexport interface ArtifactListItem {\n id: string\n kind: string\n title: string | null\n ident: string | null\n repo: string | null\n /** Repos this artifact spans — a feature's full subtree union; one entry for a PR. */\n repos: string[]\n /** Most recent linked session start; for a feature, the max across its subtree. Null if none. */\n lastSessionAt: string | null\n status: string | null\n source: string | null\n externalId: string | null\n /** PR creation time (from `gh`); null when not captured (offline / pre-backfill). */\n createdAt: string | null\n completedAt: string | null\n parentId: string | null\n complexity: number | null\n complexityBasis: string | null\n sessions: number\n costUsd: number\n /** Max content-match AI-attribution fraction across the artifact's session links (0–1);\n * null when no content-match link exists (e.g. an explicit-only PR). PRs only. */\n aiPct: number | null\n}\n\n/** One window's worth of headline KPIs (see Store.kpis). */\nexport interface KpiSnapshot {\n sessions: number\n totalSpend: number\n /** Fraction of sessions judged success; null when the window has no sessions. */\n successRate: number | null\n /** Tool-call error rate (fraction); null when the window has no tool calls. */\n errorRate: number | null\n costPerFeature: { count: number; costPerUnit: number | null }\n costPerPr: { count: number; costPerUnit: number | null }\n}\n\n/** One bucket of a success-rate series: the cohort's numerator/denominator/rate. */\nexport interface RatePoint {\n bucket: string\n num: number\n denom: number\n /** Total session cost (USD) in the bucket — feeds the per-value cost table\n * (total spend, and $/session = spend/denom). Summed over the SAME population\n * as `denom` (all sessions, not just successful ones), so spend/denom is an\n * honest avg cost per session. */\n spend: number\n /** num/denom, or null when the bucket has no sessions (drawn as a gap). */\n rate: number | null\n}\n\n/** A success-rate line: per-bucket points plus the windowed totals/rate. */\nexport interface RateSeries {\n key: string\n points: RatePoint[]\n num: number\n denom: number\n /** Window-total session cost (USD); spend/denom = avg cost per session. */\n spend: number\n rate: number | null\n}\n\nexport interface SuccessRateQuery {\n /** Outcome types counting as success (numerator). Empty → ['session_success']. */\n outcomes: string[]\n bucket: Bucket\n /** Facet key to split into one series per value (top-K by volume). */\n by?: string\n from?: string\n to?: string\n /** Session-level facet filters (multi-value OR within a facet), applied to\n * numerator and denominator alike. */\n filters?: Record<string, string[]>\n topK?: number\n}\n\nexport interface SuccessRateResult {\n outcomes: string[]\n bucket: Bucket\n /** The x-axis: every bucket label the overall line spans. */\n buckets: string[]\n overall: RateSeries\n /** Present when `by` is set. */\n series?: RateSeries[]\n /** Set when more facet values existed than were drawn. */\n truncated?: { shown: number; total: number }\n}\n\n/** One bucket of an operational (tool-call) series: count + error split. */\nexport interface OpsPoint {\n bucket: string\n /** The plotted metric: call count (count views) or error rate (rate view), null if no calls. */\n value: number | null\n calls: number\n errors: number\n}\n\nexport interface OpsSeries {\n key: string\n /** Display label when the key isn't already human-readable (error categories). */\n label?: string\n points: OpsPoint[]\n /** Total count, or overall error rate, depending on the view. */\n total: number | null\n /** Call volume — used to rank series (top-K most-used tools/skills). */\n calls: number\n}\n\nexport interface OpsOverTimeQuery {\n /** tool_calls = count all; error_rate = AVG(is_error); skill_usage = count where action='skill'. */\n view: 'tool_calls' | 'error_rate' | 'skill_usage'\n bucket: Bucket\n /** 'name' splits by tool name; 'error_category' decomposes the rate by category. */\n by?: string\n from?: string\n to?: string\n /** Generic session-level facet filters (harness/repo/model); unused by the Ops UI today. */\n filters?: Record<string, string[]>\n /** Row-level scope: only count calls of these tool names (denominator + numerator). */\n toolNames?: string[]\n /** Row-level scope: only these categories count as errors (numerator only). */\n errorCategories?: string[]\n topK?: number\n}\n\nexport interface OpsOverTimeResult {\n view: string\n bucket: Bucket\n /** The active breakdown dimension, echoed back so the client can label series. */\n by?: string\n buckets: string[]\n overall: { points: OpsPoint[]; total: number | null }\n series?: OpsSeries[]\n truncated?: { shown: number; total: number }\n format: 'int' | 'pct'\n}\n\n/** One bucket of a session-count series. */\nexport interface CountPoint {\n bucket: string\n count: number\n}\n\nexport interface CountSeries {\n key: string\n points: CountPoint[]\n total: number\n}\n\nexport interface SessionsOverTimeQuery {\n bucket: Bucket\n by?: string\n from?: string\n to?: string\n filters?: Record<string, string[]>\n topK?: number\n}\n\nexport interface SessionsOverTimeResult {\n bucket: Bucket\n buckets: string[]\n overall: { points: CountPoint[]; total: number }\n series?: CountSeries[]\n truncated?: { shown: number; total: number }\n}\n\n/** One bucket of a spend series. */\nexport interface SpendPoint {\n bucket: string\n spend: number\n}\n\n/** A spend line: per-bucket points plus the total over the range. */\nexport interface SpendSeries {\n key: string\n points: SpendPoint[]\n total: number\n}\n\nexport interface SpendOverTimeQuery {\n bucket: Bucket\n /** Facet key to split into one series per value (top-K by total spend). */\n by?: string\n from?: string\n to?: string\n filters?: Record<string, string[]>\n topK?: number\n}\n\nexport interface SpendOverTimeResult {\n bucket: Bucket\n buckets: string[]\n overall: { points: SpendPoint[]; total: number }\n series?: SpendSeries[]\n truncated?: { shown: number; total: number }\n /** True when the breakdown facet is multi-valued, so series sum past overall. */\n presenceInflated?: boolean\n}\n\nexport type Bucket = 'day' | 'week' | 'month'\n\nexport interface TimePoint {\n bucket: string\n sessions: number\n spend: number\n shipped: number\n}\n\nexport interface SessionFilter {\n /** facetKey -> value; compiled to predicates via the facet registry. */\n facets?: Record<string, string>\n q?: string\n /** Match sessions linked to an artifact whose path/PR/url/repo/feature-title matches. */\n artifact?: string\n /** Restrict the artifact match to a kind: file | pr | feature | ticket | commit. */\n artifactKind?: string\n /** Window on session start (ISO); inclusive lower / exclusive upper bound. */\n from?: string\n to?: string\n /** Match sessions that produced ANY of these outcome types (OR). */\n outcomeTypes?: string[]\n limit?: number\n}\n\nexport interface SessionListItem {\n id: string\n title: string\n startedAt: string | null\n costUsd: number\n models: string[]\n complexity: string | null\n useCase: string[]\n intent: string | null\n /** Distinct outcome types this session produced (e.g. pr_merged, session_success). */\n outcomes: string[]\n}\n\nexport interface TranscriptTool {\n name: string\n action: string\n ok: boolean\n /** This call's index in session.toolCalls — the transcript anchors a failed call as `txerr-<idx>`. */\n idx?: number\n target?: string\n /** Full tool input rendered as displayable text (key field or JSON). */\n command?: string\n /** Tool output/result text (clipped to OUTPUT_MAX, with an explicit tail notice if cut). */\n output?: string\n /** For Edit/Write: old→new hunks for inline diff rendering. */\n hunks?: { del: string; ins: string }[]\n error?: string\n /** For a subagent-spawning call (`Task`/`Agent`), the agentId it links to. */\n agentId?: string\n}\n\nexport interface TranscriptTurn {\n role: 'user' | 'assistant' | 'system'\n ts?: string\n sidechain: boolean\n /** Which subagent emitted this turn; undefined for main-thread turns. */\n agentId?: string\n /** Main-thread sequence index (undefined for sidechain turns). */\n seq?: number\n /** Block this turn belongs to (handling_long_sessions); undefined if unmapped. */\n blockIdx?: number\n text: string\n tools: TranscriptTool[]\n}\n\n/** One subagent's identity, for the transcript's per-subagent tab + spawn link. */\nexport interface SubagentInfo {\n agentId: string\n agentType?: string\n description?: string\n /** tool_use id of the spawning call in the parent thread (absent for workflow subagents). */\n toolUseId?: string\n}\n\n/**\n * A session's viewer-ready transcript: one flat, globally-indexed list of turns\n * (each tagged with its `agentId`, so the client can split the main thread from\n * each subagent into its own tab) plus the subagent roster.\n */\n/** One block's identity + labels, for the transcript's filter bar. */\nexport interface TranscriptBlock {\n idx: number\n useCase?: string | null\n pr?: { ident: string; title?: string } | null\n feature?: { id: string; title?: string } | null\n}\n\nexport interface Transcript {\n turns: TranscriptTurn[]\n subagents: SubagentInfo[]\n /** Block partition + per-block labels, for filtering the transcript by PR / feature / use-case. */\n blocks: TranscriptBlock[]\n}\n\n/** A facet's resolved value for one session — the registry-driven detail row. */\nexport interface FacetValue {\n key: string\n label: string\n type: FacetType\n /** scalar, list (multi / child-grain facets), or null when the session has none. */\n value: string | string[] | null\n}\n\nexport interface SessionDetail {\n session: Record<string, unknown>\n annotations: Record<string, unknown>\n outcomes: Array<{ type: string; artifactId: string | null }>\n artifacts: Array<Record<string, unknown>>\n facets: FacetValue[]\n transcript: Transcript\n}\n\n/**\n * One successful file write in the session — a before/after (Edit), full content\n * (Write), or hunks (MultiEdit). Returned as a flat, chronological list so the\n * client can group it either by file or by prompt.\n */\nexport interface FileEdit {\n path: string\n op: 'edit' | 'multiedit' | 'write'\n hunks: Array<{ del: string; ins: string }>\n ts?: string\n /** Index into the transcript turns of the assistant turn that made the edit. */\n turn: number\n /** Index of the preceding (non-synthetic) user turn — the prompting intent, or -1. */\n userTurn: number\n}\n\nfunction bucketExpr(col: string, bucket: Bucket): string {\n if (bucket === 'day') return `date(${col})`\n if (bucket === 'month') return `strftime('%Y-%m', ${col})`\n return `strftime('%Y-W%W', ${col})`\n}\n\n/**\n * Precedence of a PR-kind block attribution — higher wins the single row that block keeps\n * (cross-role 1-1, applied in persistResult). 0 = not a ranked PR row (e.g. enrich's\n * feature-contributed, a different artifact kind that coexists). Order of the ranks:\n * pcm content-match (the code literally shipped) > og explicit review > enrich derived\n * review > og proximity-fill contribution.\n */\nfunction prBlockRank(producer: string, role: string | null | undefined): number {\n if (producer === 'pr-content-match') return 4\n if (producer === 'outcomes-git') return role === 'reviewed' ? 3 : 1\n if (producer === 'enrich-session' && role === 'reviewed') return 2\n return 0\n}\n\nexport type ComplexityBucket = 'trivial' | 'small' | 'medium' | 'large' | 'xl'\n\nconst COMPLEXITY_RANGES: Record<ComplexityBucket, [number, number]> = {\n trivial: [0, 10],\n small: [11, 100],\n medium: [101, 500],\n large: [501, 1500],\n xl: [1501, 999999999],\n}\n\nconst FEATURE_COMPLEXITY: Record<string, number> = {\n trivial: 1, small: 2, medium: 3, large: 4, xl: 5,\n}\n\nfunction complexityWhere(bucket: string | undefined, alias: string, kind?: string): string {\n if (!bucket) return ''\n const buckets = bucket.split(',').filter(Boolean)\n if (!buckets.length) return ''\n if (kind !== 'feature' && kind !== 'pr') return ''\n const clauses: string[] = []\n let hasNone = false\n for (const b of buckets) {\n if (b === 'none') { hasNone = true; continue }\n if (kind === 'feature') {\n const val = FEATURE_COMPLEXITY[b]\n if (val) clauses.push(`${alias}.complexity = ${val}`)\n } else {\n const range = COMPLEXITY_RANGES[b as ComplexityBucket]\n if (range) clauses.push(`(${alias}.complexity >= ${range[0]} AND ${alias}.complexity <= ${range[1]})`)\n }\n }\n if (hasNone) clauses.push(`${alias}.complexity IS NULL`)\n if (!clauses.length) return ''\n return `AND (${clauses.join(' OR ')})`\n}\n\n// Display title: the enrichment-derived `title` annotation, else the native adapter title\nfunction titleExpr(alias: string): string {\n return `COALESCE((SELECT json_extract(value,'$') FROM annotations WHERE session_id=${alias}.id AND key='title'), ${alias}.title)`\n}\n\nfunction safeJson<T>(s: unknown, fallback: T): T {\n if (typeof s !== 'string') return fallback\n try {\n return JSON.parse(s) as T\n } catch {\n return fallback\n }\n}\n\nfunction rowToFacet(r: Record<string, any>): FacetSpec {\n return {\n key: r.key,\n label: r.label ?? undefined,\n type: r.type,\n source: r.source,\n column: r.col ?? undefined,\n base: r.base ?? undefined,\n multi: !!r.multi,\n roles: safeJson(r.roles, [] as FacetSpec['roles']),\n }\n}\n\nfunction rowToMeasure(r: Record<string, any>): MeasureSpec {\n return {\n key: r.key,\n label: r.label ?? undefined,\n source: r.source,\n expr: r.expr,\n agg: r.agg,\n base: r.base ?? undefined,\n format: r.format ?? undefined,\n }\n}\n\nfunction aggExpr(m: MeasureSpec): string {\n switch (m.agg) {\n case 'sum':\n return `SUM(${m.expr})`\n case 'avg':\n return `AVG(${m.expr})`\n case 'count':\n return 'COUNT(*)'\n case 'count_distinct':\n return `COUNT(DISTINCT ${m.expr})`\n case 'rate':\n return `AVG(CASE WHEN ${m.expr} THEN 1.0 ELSE 0.0 END)`\n }\n}\n\n/**\n * Group expression + any join/where a facet contributes to a breakdown. `anchorGrain`\n * is the MEASURE's grain (alias s/u/t) — needed to bridge a block facet up from the\n * measure's own anchor (usage_facts / tool_calls) through the block membership tables.\n */\nfunction facetGroupExpr(f: FacetSpec, anchorGrain: Grain): { join: string; expr: string; where?: string } {\n const col = f.column ?? f.key\n if (f.source === 'session') {\n return f.multi\n ? { join: `, json_each(s.${col}) fje`, expr: 'fje.value' }\n : { join: '', expr: `s.${col}` }\n }\n if (f.source === 'annotation') {\n return f.multi\n ? {\n join: `JOIN annotations fa ON fa.session_id = s.id AND fa.key = '${f.key}' JOIN json_each(fa.value) fje`,\n expr: 'fje.value',\n }\n : {\n join: `JOIN annotations fa ON fa.session_id = s.id AND fa.key = '${f.key}'`,\n expr: `json_extract(fa.value,'$')`,\n }\n }\n if (f.source === 'block') {\n // Bridge the measure's anchor (usage_facts / tool_calls) up to its block, then to\n // the per-block label. Only reached for usage/tool_call measures (block is their\n // ancestor); session measures can't group by a finer block facet (guard rejects).\n const anchor = aliasFor(anchorGrain)\n const member = anchorGrain === 'tool_call' ? 'block_tool' : 'block_usage'\n const memberCol = anchorGrain === 'tool_call' ? 'tool_idx' : 'usage_idx'\n return {\n join:\n `JOIN ${member} bm ON bm.session_id = ${anchor}.session_id AND bm.${memberCol} = ${anchor}.idx ` +\n `JOIN block_annotations ba ON ba.session_id = bm.session_id AND ba.block_idx = bm.block_idx AND ba.key = '${f.key}'`,\n expr: `json_extract(ba.value,'$')`,\n }\n }\n // same-grain child facet (usage / tool-call): a column on the measure's own anchor\n return { join: '', expr: `${aliasFor(grainOf(f.source))}.${col}`, where: f.base }\n}\n\nfunction buildTranscript(session: Session): Transcript {\n const turns = buildTranscriptCore(session).turns\n // Map each main-thread turn to its block via seq (labels are attached in\n // sessionDetail, which has DB access). Deterministic, so idx matches storage.\n const blocks = deterministicBlocks(session)\n if (blocks.length) {\n const maxSeq = blocks[blocks.length - 1]!.endSeq\n const seqToBlock = new Array<number>(maxSeq + 1).fill(-1)\n for (const b of blocks) for (let s = b.startSeq; s <= b.endSeq; s++) seqToBlock[s] = b.idx\n for (const t of turns) {\n if (t.seq == null || t.seq < 0 || t.seq > maxSeq) continue\n const bi = seqToBlock[t.seq]\n if (bi != null && bi >= 0) t.blockIdx = bi\n }\n }\n return {\n turns,\n subagents: (session.subagents ?? []).map((s) => ({\n agentId: s.agentId,\n agentType: s.agentType,\n description: s.description,\n toolUseId: s.toolUseId,\n })),\n blocks: blocks.map((b) => ({ idx: b.idx })),\n }\n}\n\n/**\n * Transcript turns PLUS a map from each tool_use id to its turn index and the\n * index of the preceding user turn — so the Files-changed view can link an edit\n * back to the user message that prompted it (the \"intent\"). Turn indices match\n * positions in `turns`, which is exactly what the client anchors as `txt-<i>`.\n */\nfunction buildTranscriptCore(session: Session): {\n turns: TranscriptTurn[]\n toolTurn: Map<string, { turn: number; userTurn: number }>\n} {\n const tcById = new Map(session.toolCalls.map((t) => [t.id, t]))\n const idxById = new Map(session.toolCalls.map((t, i) => [t.id, i]))\n // toolUseId of a spawning call → the subagent it spawned, so the main-thread\n // chip can link to that subagent's transcript tab.\n const spawnToAgent = new Map<string, string>()\n for (const sa of session.subagents ?? []) if (sa.toolUseId) spawnToAgent.set(sa.toolUseId, sa.agentId)\n const turns: TranscriptTurn[] = []\n const toolTurn = new Map<string, { turn: number; userTurn: number }>()\n let lastUserIdx = -1\n for (const ev of session.events) {\n if (ev.kind === 'assistant') {\n let text = ''\n const tools: TranscriptTool[] = []\n const ids: string[] = []\n for (const b of ev.blocks) {\n if (b.type === 'text') text += (text ? '\\n' : '') + b.text\n else if (b.type === 'tool_use') {\n ids.push(b.id)\n const tc = tcById.get(b.id)\n const input = b.input as Record<string, unknown> | undefined\n // Prefer the adapter-normalized target (paths/command) over re-parsing the\n // raw input blob, so per-vendor field spellings live in the adapter.\n const target = tc?.target.paths?.[0] ?? tc?.target.command\n const res = tc?.result\n const ok = res ? res.ok : true\n const command = toolCommandText(tc, input)\n const output = res?.raw != null ? clipOutput(readableOutput(tc?.action, res.raw)) : undefined\n const tool: TranscriptTool = { name: b.name, action: '', ok, idx: idxById.get(b.id), target: clip(target, 1500) }\n if (command) tool.command = command\n if (output) tool.output = output\n // file_write → inline diff. The before/after strings aren't in the\n // normalized target, so read them from the raw input here; field names\n // differ across harnesses (Claude Code: old_string/new_string;\n // OpenCode: oldString/newString).\n if (tc?.action === 'file_write' && input) {\n const old_s = clip(String(input.old_string ?? input.oldString ?? ''), 2000)\n const new_s = clip(String(input.new_string ?? input.newString ?? ''), 2000)\n if (old_s || new_s) tool.hunks = [{ del: old_s, ins: new_s }]\n else {\n const content = clip(String(input.content ?? ''), 2000)\n if (content) tool.hunks = [{ del: '', ins: content }]\n }\n }\n if (!ok) tool.error = clipError(resultText(res?.raw))\n const spawned = spawnToAgent.get(b.id)\n if (spawned) tool.agentId = spawned\n tools.push(tool)\n }\n }\n if (!text && tools.length === 0) continue\n const idx = turns.length\n for (const id of ids) toolTurn.set(id, { turn: idx, userTurn: lastUserIdx })\n turns.push({ role: 'assistant', ts: ev.ts, sidechain: ev.isSidechain, agentId: ev.agentId, seq: ev.seq, text: clip(text, 20000), tools })\n } else if (ev.kind === 'user') {\n const text = ev.text.replace(/<system-reminder>[\\s\\S]*?<\\/system-reminder>/gi, ' ').trim()\n if (!text) continue\n // Only real prompts become the \"intent\" an edit links back to (the turn is\n // still shown in the transcript, just not used as a jump/grouping target).\n if (!isSyntheticUser(text)) lastUserIdx = turns.length\n turns.push({ role: 'user', ts: ev.ts, sidechain: ev.isSidechain, agentId: ev.agentId, seq: ev.seq, text: clip(text, 20000), tools: [] })\n }\n }\n return { turns, toolTurn }\n}\n\n/** Extract the meaningful output text for a tool, handling action-specific payloads. */\nfunction readableOutput(action: CanonicalAction | undefined, raw: unknown): string {\n if (raw == null) return ''\n if (action === 'file_read' && typeof raw === 'object' && !Array.isArray(raw)) {\n const o = raw as Record<string, unknown>\n // Claude Code's Read result is {\"type\":\"text\",\"file\":{\"filePath\":\"...\",\"content\":\"...\"}};\n // OpenCode stores a plain string, which falls through to resultText below.\n const file = o.file as Record<string, unknown> | undefined\n if (file && typeof file.content === 'string') return file.content\n }\n return resultText(raw)\n}\n\n/**\n * One-line description of a tool call for the transcript. Dispatches on the\n * adapter-assigned canonical `action` (not the raw vendor tool name), so the\n * vocabulary differences across harnesses (`Bash` vs `bash`, `Agent` vs `task`,\n * `AskUserQuestion` vs `question`) stay inside the adapters. Paths and shell\n * commands come from the already-normalized `target`; everything else reads the\n * genuinely vendor-neutral input fields (`prompt`, `pattern`, `url`, `todos`,\n * `questions`), whose spellings match across harnesses.\n */\nfunction toolCommandText(tc: ToolCall | undefined, input: Record<string, unknown> | undefined): string {\n if (!input) return ''\n switch (tc?.action) {\n case 'file_read':\n case 'file_write':\n return clip(tc.target.paths?.[0] ?? '', 2000)\n case 'shell':\n return clip(tc.target.command ?? '', 2000)\n case 'task_spawn':\n return firstStringField(input, ['prompt'])\n case 'skill':\n return firstStringField(input, ['skill', 'name'])\n case 'web':\n return firstStringField(input, ['url', 'query'])\n case 'search':\n return firstStringField(input, ['pattern', 'query', 'path'])\n case 'todo':\n return renderTodos(input)\n }\n // 'other' / 'mcp_call' / unmapped: a question prompt (AskUserQuestion /\n // OpenCode `question`) or generic query if present, else the raw input as JSON.\n const qs = input.questions\n if (Array.isArray(qs) && qs.length && typeof qs[0] === 'object' && qs[0]) {\n return String((qs[0] as Record<string, unknown>).question ?? '')\n }\n const query = firstStringField(input, ['query'])\n if (query) return query\n try {\n return clip(JSON.stringify(input, null, 2), 2000)\n } catch {\n return ''\n }\n}\n\n/** First non-empty string among the given input keys, clipped for display. */\nfunction firstStringField(input: Record<string, unknown>, keys: string[]): string {\n for (const k of keys) {\n const val = input[k]\n if (typeof val === 'string' && val) return clip(val, 2000)\n }\n return ''\n}\n\n/** Render a todo-list tool's items as a checkbox summary. */\nfunction renderTodos(input: Record<string, unknown>): string {\n const todos = input.todos\n if (!Array.isArray(todos) || !todos.length) return ''\n const lines = todos.map((t) => {\n const o = (t && typeof t === 'object' ? t : {}) as Record<string, unknown>\n const status = typeof o.status === 'string' ? o.status : ''\n const mark = status === 'completed' ? '✓' : status === 'in_progress' ? '▶' : '○'\n return `${mark} ${typeof o.content === 'string' ? o.content : ''}`\n })\n return clip(lines.join('\\n'), 2000)\n}\n\nfunction clip(s: string | undefined, n: number): string {\n if (!s) return ''\n return s.length > n ? s.slice(0, n) + ' …' : s\n}\n\n/**\n * Clip tool output for the transcript viewer. Generous cap (the detail payload is\n * built per-session on demand, so this only ships for the one session being viewed),\n * and the tail notice makes the cut explicit rather than a silent \" …\".\n */\nconst OUTPUT_MAX = 20000\nfunction clipOutput(s: string): string {\n if (!s) return ''\n if (s.length <= OUTPUT_MAX) return s\n return s.slice(0, OUTPUT_MAX) + `\\n\\n… ${s.length - OUTPUT_MAX} more characters truncated …`\n}\n\n/**\n * Clip a long tool error keeping BOTH ends: the head (e.g. \"Error: Exit code 1\")\n * and the tail, where the actual failure usually is. The UI auto-scrolls the\n * panel to the bottom so that tail is what you see first.\n */\nfunction clipError(s: string): string {\n const MAX = 1000\n if (s.length <= MAX) return s\n const head = 160\n const tail = MAX - head\n return s.slice(0, head).trimEnd() + `\\n … ${s.length - head - tail} chars omitted … \\n` + s.slice(-tail).trimStart()\n}\n\n/** Best-effort readable text from a tool result's raw payload (string, content-block array, or object). */\nfunction resultText(raw: unknown): string {\n if (raw == null) return ''\n if (typeof raw === 'string') return raw\n if (Array.isArray(raw)) {\n return raw\n .map((b) => (typeof b === 'string' ? b : b && typeof b === 'object' && 'text' in b ? String((b as { text: unknown }).text) : ''))\n .filter(Boolean)\n .join('\\n')\n }\n if (typeof raw === 'object') {\n const o = raw as Record<string, unknown>\n for (const k of ['stdout', 'stderr', 'error', 'message', 'content']) {\n const v = o[k]\n if (typeof v === 'string' && v) return v\n if (Array.isArray(v)) return resultText(v)\n }\n try {\n return JSON.stringify(o)\n } catch {\n return ''\n }\n }\n return String(raw)\n}\n","import type { FileEdit } from './store'\n\n/** The file-diff fields `parseApplyPatch` derives; the caller stamps ts/turn. */\nexport type PatchFileEdit = Pick<FileEdit, 'path' | 'op' | 'hunks'>\n\n// Match the per-side caps fileChanges() uses for object-shaped edits, so a Codex\n// patch and a Claude/OpenCode edit render with the same window.\nconst WRITE_CAP = 16000\nconst EDIT_CAP = 4000\nfunction clip(s: string, n: number): string {\n return s.length > n ? s.slice(0, n) + ' …' : s\n}\n\n/**\n * Parse a Codex `apply_patch` V4A patch — the raw `*** Begin Patch … *** End Patch`\n * text Codex stores as the tool-call input — into one FileEdit per file it touches.\n *\n * Codex differs from Claude Code / OpenCode in two ways the generic object-shaped\n * path in fileChanges() can't handle: the input is patch TEXT (not `{content}` /\n * `{old_string,new_string}`), and a single call bundles MANY files. Left to the\n * object path it collapses to one empty diff — the \"5 files, one shown, no content\"\n * symptom. Mapping per file:\n * - `*** Add File:` → a `write` of the added (`+`) lines.\n * - `*** Update File:` → `edit` / `multiedit`, one hunk per `@@` section; context\n * lines go to BOTH sides so the client's line diff re-derives the +/−.\n * - `*** Delete File:` → an empty `edit` (the patch carries no prior content, so\n * only the path is surfaced).\n * - `*** Move to:` → renames the current file to the new path.\n */\nexport function parseApplyPatch(patch: string): PatchFileEdit[] {\n const out: PatchFileEdit[] = []\n type Cur = {\n path: string\n mode: 'add' | 'update' | 'delete'\n addLines: string[]\n hunks: Array<{ del: string[]; ins: string[] }>\n }\n let cur: Cur | null = null\n\n const flush = (): void => {\n if (!cur) return\n if (cur.mode === 'add') {\n out.push({ path: cur.path, op: 'write', hunks: [{ del: '', ins: clip(cur.addLines.join('\\n'), WRITE_CAP) }] })\n } else if (cur.mode === 'delete') {\n out.push({ path: cur.path, op: 'edit', hunks: [{ del: '', ins: '' }] })\n } else {\n const hunks = cur.hunks\n .filter((h) => h.del.length || h.ins.length)\n .map((h) => ({ del: clip(h.del.join('\\n'), EDIT_CAP), ins: clip(h.ins.join('\\n'), EDIT_CAP) }))\n if (hunks.length) out.push({ path: cur.path, op: hunks.length > 1 ? 'multiedit' : 'edit', hunks })\n }\n cur = null\n }\n\n for (const raw of patch.split('\\n')) {\n const add = /^\\*\\*\\* Add File: (.+)$/.exec(raw)\n const upd = /^\\*\\*\\* Update File: (.+)$/.exec(raw)\n const del = /^\\*\\*\\* Delete File: (.+)$/.exec(raw)\n const mov = /^\\*\\*\\* Move to: (.+)$/.exec(raw)\n if (add) {\n flush()\n cur = { path: add[1]!.trim(), mode: 'add', addLines: [], hunks: [] }\n continue\n }\n if (upd) {\n flush()\n cur = { path: upd[1]!.trim(), mode: 'update', addLines: [], hunks: [{ del: [], ins: [] }] }\n continue\n }\n if (del) {\n flush()\n cur = { path: del[1]!.trim(), mode: 'delete', addLines: [], hunks: [] }\n continue\n }\n if (mov) {\n if (cur) cur.path = mov[1]!.trim() // rename → surface the new path\n continue\n }\n if (/^\\*\\*\\* (?:Begin|End) Patch/.test(raw)) continue\n if (!cur) continue\n\n if (cur.mode === 'add') {\n // Add bodies are all `+` lines; tolerate a stray bare line.\n cur.addLines.push(raw.startsWith('+') ? raw.slice(1) : raw)\n continue\n }\n if (cur.mode === 'update') {\n if (raw.startsWith('@@')) {\n // Section boundary → start a fresh hunk (skip if the current one is empty).\n const last = cur.hunks[cur.hunks.length - 1]!\n if (last.del.length || last.ins.length) cur.hunks.push({ del: [], ins: [] })\n continue\n }\n const h = cur.hunks[cur.hunks.length - 1]!\n const tag = raw[0]\n const body = raw.slice(1)\n if (tag === '+') h.ins.push(body)\n else if (tag === '-') h.del.push(body)\n else h.del.push(body), h.ins.push(body) // ' ' context (and bare/blank lines) → both sides\n }\n }\n flush()\n return out\n}\n","/**\n * Cross-harness fingerprinting of failed tool calls into a small, fixed taxonomy.\n *\n * One shared, deterministic classifier runs over the NORMALIZED error text (the\n * same `result.raw` the transcript viewer reads) so Codex / Claude / OpenCode\n * errors land in the SAME categories — the whole point is harness-agnostic error\n * analytics. The error STRINGS are vendor-specific (e.g. \"File has not been read\n * yet\" is Claude's phrasing); the classifier folds each vendor's phrasings into a\n * shared category. As new harnesses appear, add their phrasings to existing\n * buckets, not new categories.\n *\n * Deterministic (regex), not LLM: these strings are highly patterned, a regex\n * pass classifies ~96% of real errors, the result must be stable across\n * re-ingest (it drives a filter/breakdown facet), and it is computed at ingest\n * for EVERY session — with no LLM key required. Re-ingest is gated by\n * NORMALIZE_VERSION (core/blocks.ts): bump it to re-fingerprint all harnesses.\n *\n * `other` is the deliberate catch-all (errored but unrecognized).\n */\nimport type { CanonicalAction } from './model'\n\nexport interface ErrorCategorySpec {\n key: string\n label: string\n /** One-line tooltip shown next to the category in the UI. */\n description: string\n}\n\n/**\n * The frozen taxonomy: each category's label + tooltip description. This array's\n * order is DISPLAY order (it drives the /api/error-categories response), NOT match\n * precedence — precedence is the order the regexes are tested in classifyError\n * below. Keep `other` last as the catch-all.\n */\nexport const ERROR_CATEGORIES: ErrorCategorySpec[] = [\n {\n key: 'precondition',\n label: 'Precondition',\n description: \"The tool's preconditions weren't met - e.g. editing a file before reading it, or the file changed since it was read.\",\n },\n {\n key: 'not_found',\n label: 'Not found',\n description: \"A referenced file, path, or resource didn't exist (ENOENT, 404, 'not found').\",\n },\n {\n key: 'permission',\n label: 'Permission',\n description: 'The filesystem or OS denied access (EACCES, permission denied).',\n },\n {\n key: 'policy_blocked',\n label: 'Policy blocked',\n description: 'The host harness blocked the action - approval required, sandbox policy, or a denied/blocked command.',\n },\n {\n key: 'user_rejected',\n label: 'User declined',\n description: 'The user declined an approval prompt - the tool never ran (e.g. \"User rejected tool use\").',\n },\n {\n key: 'auth',\n label: 'Auth',\n description: 'Authentication or credentials failed - invalid/expired token, 401, bad credentials.',\n },\n {\n key: 'conflict',\n label: 'VCS conflict',\n description: 'A version-control conflict - merge conflict, non-fast-forward, rejected push.',\n },\n {\n key: 'test_failure',\n label: 'Test failure',\n description: 'A test run reported failures or assertion errors.',\n },\n {\n key: 'compile_error',\n label: 'Compile error',\n description: 'Code failed to compile or type-check - syntax errors, missing modules, type errors.',\n },\n {\n key: 'invalid_call',\n label: 'Invalid call',\n description: 'The tool was called incorrectly - unknown tool/skill, bad arguments, or invalid options.',\n },\n {\n key: 'schema_validation',\n label: 'Schema validation',\n description: \"A tool's structured output failed schema validation - a required field was missing or mistyped.\",\n },\n {\n key: 'network',\n label: 'Network',\n description: 'A network problem - connection refused, DNS failure, fetch failed.',\n },\n {\n key: 'timeout',\n label: 'Timeout',\n description: 'The operation exceeded its time limit.',\n },\n {\n key: 'rate_limit',\n label: 'Rate limit',\n description: 'A provider rate limit or quota was hit (429, too many requests).',\n },\n {\n key: 'too_large',\n label: 'Too large',\n description: \"Input or output exceeded the tool's size limit.\",\n },\n {\n key: 'interrupted',\n label: 'Interrupted',\n description: 'The call was cancelled, aborted, or killed before finishing.',\n },\n {\n key: 'integration_error',\n label: 'Integration error',\n description: 'An external tool or MCP server failed to respond or errored at the transport level.',\n },\n {\n key: 'command_failed',\n label: 'Command failed',\n description: 'A shell command exited non-zero with no more specific cause detected.',\n },\n {\n key: 'other',\n label: 'Other',\n description: \"Errored, but didn't match a known category.\",\n },\n]\n\nexport type ErrorCategory = (typeof ERROR_CATEGORIES)[number]['key']\n\n/**\n * Map a failed tool call's normalized error text to one category. The regexes are\n * tested top-to-bottom and the FIRST match wins, so their order here IS the match\n * precedence: specific / agent-behaviour categories first, the broad\n * `command_failed` shell fallback last. Returns `other` when nothing matches\n * (incl. empty text).\n */\nexport function classifyError(_action: CanonicalAction, text: string): ErrorCategory {\n const t = (text || '').toLowerCase()\n if (!t.trim()) return 'other'\n\n // User declined an approval prompt — a deliberate user choice, not a tool\n // failure, and the single most common \"error\" shape in real transcripts. Must\n // come FIRST: the harness phrasings contain the bare word \"rejected\", which\n // would otherwise be swept into the VCS `conflict` bucket below. Claude:\n // \"User rejected tool use\" / \"doesn't want to proceed\". Codex frames a denied\n // approval as `CreateProcess { message: \"Rejected(\"rejected by user\")\" }`.\n if (/user rejected|doesn'?t want to proceed|tool use was rejected|rejected by user|rejected\\(\"/.test(t)) return 'user_rejected'\n // Agent-workflow precondition (read-before-write, stale file) — the biggest real\n // bucket, so it goes first.\n if (/has not been read yet|modified since read|read it again|read it first|must read|before (?:editing|writing|attempting)|oldstring|must match exactly/.test(t)) return 'precondition'\n // Host policy / approval gating (distinct from a filesystem permission error).\n if (/permission to use .* has been denied|don'?t ask mode|\\bblocked:|requires approval|approval (?:required|denied)|not allowed by|operation not permitted by/.test(t)) return 'policy_blocked'\n // Input/output exceeded a tool size limit. (Bare \"too long\" is omitted — it\n // collides with \"took too long\" timeouts; \"argument list too long\" is kept.)\n if (/exceeds (?:the )?maximum|maximum allowed tokens|too large|output too large|argument list too long/.test(t)) return 'too_large'\n // Structured output that failed its schema (ajv-style \"must have required\n // property\" / \"does not match required schema\"). Distinct from invalid_call: the\n // OUTPUT is malformed, not the call. Before invalid_call so it wins outright.\n if (/does not match (?:the )?required schema|must have required property|failed schema validation/.test(t)) return 'schema_validation'\n // Bad invocation: unknown tool/skill, invalid arguments/options. Also the\n // schema-checked INPUT failures (zod-style InputValidationError, unparseable\n // JSON args) and EISDIR — a directory handed to a file tool — which are all\n // \"the tool was called wrong.\"\n if (/unknown skill|no such tool|tool not found|no such tool available|unknown command|unrecognized|no such option|invalid (?:argument|option|flag)|missing required|bad pattern|invalid regular expression|usage:|inputvalidationerror|json parse failed|invalid_type|\\beisdir\\b|illegal operation on a directory/.test(t)) return 'invalid_call'\n // External tool / MCP transport failure.\n if (/tool call (?:error|failed)|transport (?:send )?error|mcp error|server error/.test(t)) return 'integration_error'\n // `cannot find` excludes module/name — those are compile/dependency errors\n // (handled by compile_error below), not a missing file/path.\n if (/enoent|no such file|does not exist|cannot find (?!module|name)|file not found|\\bnot found\\b|404/.test(t)) return 'not_found'\n if (/eacces|permission denied|operation not permitted|\\bforbidden\\b|403/.test(t)) return 'permission'\n if (/401|unauthorized|bad credentials|authentication|token (?:expired|invalid)|not logged in|requires authentication/.test(t)) return 'auth'\n if (/429|rate limit|quota|too many requests/.test(t)) return 'rate_limit'\n if (/timed out|timeout|etimedout|deadline exceeded|context deadline/.test(t)) return 'timeout'\n if (/econnrefused|connection refused|enotfound|getaddrinfo|fetch failed|\\bdns\\b|socket hang up|econnreset|\\bnetwork\\b/.test(t)) return 'network'\n // VCS conflict — anchored to real git output. Bare `conflict`/`rejected` are\n // deliberately avoided: they hijacked unrelated text (e.g. \"User rejected tool\n // use\"). Git's push rejection prints `! [rejected]` / \"Updates were rejected\".\n if (/merge conflict|non-fast-forward|failed to push|diverged|\\[rejected\\]|updates were rejected/.test(t)) return 'conflict'\n if (/\\d+ (?:failed|failing)|test(?:s)? failed|assertionerror|expect\\(|✕/.test(t)) return 'test_failure'\n if (/syntaxerror|cannot find (?:module|name)|ts\\d{3,4}\\b|error ts|type error|is not assignable|compilation|unexpected token|parse error/.test(t)) return 'compile_error'\n if (/sigint|\\bcancel|aborted|interrupted|\\bkilled\\b/.test(t)) return 'interrupted'\n // Generic shell non-zero exit with no recognized cause — the honest fallback.\n if (/exit code|exited with code|process exited|exit status|non-zero/.test(t)) return 'command_failed'\n return 'other'\n}\n","/**\n * The facet registry: the single source of truth for the categorical dimensions\n * the dashboard charts, filters, and (later) compares by.\n *\n * A facet's `source` names WHERE its value lives — which also implies its grain.\n * `multi` is cardinality (array vs scalar). `type` is the element type. The query\n * builder (Store.facetDistribution / facetPredicate) derives the exact read shape\n * — raw column / json_extract / json_each / EXISTS — from `(source, multi)`, so\n * nothing above the store hardcodes which dimensions exist.\n *\n * Two sources of facets, both persisted to the `facets` table at analyze time so\n * the separate serve process can read them without importing processors:\n * - intrinsic facets (below): structural, present without any processor\n * - processor-declared facets: a processor's `facets` field (e.g. enrichment)\n */\n\n/** Where a facet's value lives — implies its grain. */\nexport type FacetSource = 'session' | 'annotation' | 'tool-call' | 'usage' | 'block'\nexport type FacetType = 'string' | 'number' | 'boolean' | 'enum'\n\n/**\n * The entity a row lives at. Ancestry: session ⊃ block ⊃ {usage, tool_call};\n * usage and tool_call are siblings (both children of block).\n */\nexport type Grain = 'session' | 'block' | 'usage' | 'tool_call'\n\n/** A source's grain. session-column and annotation are both per-session. */\nexport function grainOf(source: FacetSource): Grain {\n return source === 'usage'\n ? 'usage'\n : source === 'tool-call'\n ? 'tool_call'\n : source === 'block'\n ? 'block'\n : 'session'\n}\n\n/** Depth in the grain tree (coarsest = 0). usage and tool_call share depth 2. */\nexport const GRAIN_DEPTH: Record<Grain, number> = { session: 0, block: 1, usage: 2, tool_call: 2 }\n\n/**\n * Whether a facet at grain `gf` is a valid GROUP BY for a measure at grain `gm`:\n * `gf` must equal `gm` or be a strict ANCESTOR (session/block). Finer or sibling\n * facets (e.g. cost × skill = usage × tool_call) are rejected to avoid silent\n * double-counting — they need the (unbuilt) pre-reduction path.\n */\nexport function facetGroupCompatible(gf: Grain, gm: Grain): boolean {\n if (gf === gm) return true\n if (GRAIN_DEPTH[gf] >= GRAIN_DEPTH[gm]) return false\n return gf === 'session' || gf === 'block'\n}\n/** Where a facet may surface in the UI. */\nexport type FacetRole = 'chart' | 'filter' | 'detail'\n\nexport interface FacetSpec {\n key: string\n label?: string\n /** Element type (drives rendering); never 'array' — array-ness is `multi`. */\n type: FacetType\n source: FacetSource\n /**\n * Physical column for session / tool-call / usage facets; defaults to `key`.\n * Unused for `annotation` (there `key` IS the annotation key).\n */\n column?: string\n /** Base predicate scoping rows for tool-call / usage facets, e.g. action='skill'. */\n base?: string\n /**\n * Array-valued (json_each) vs scalar. Only meaningful for session/annotation\n * storage; for tool-call/usage the to-many-ness is intrinsic to the grain.\n */\n multi?: boolean\n roles?: FacetRole[]\n}\n\n/** Structural facets that exist without any processor having run. */\nexport const INTRINSIC_FACETS: FacetSpec[] = [\n {\n // The harness a session came from (sessions.source: claude-code | codex | …).\n // Session-grain, so it splits both session counts/rates and usage measures (cost).\n key: 'harness',\n label: 'Harness',\n type: 'enum',\n source: 'session',\n column: 'source',\n roles: ['chart', 'filter', 'detail'],\n },\n { key: 'repo', label: 'Repo', type: 'string', source: 'session', column: 'repo', roles: ['chart', 'filter', 'detail'] },\n {\n // Usage-grain (usage_facts.model), NOT the sessions.models array — so a usage\n // MEASURE (cost/tokens) groups by model with a correct per-model split, while\n // distribution/filter still read \"sessions that used model X\" via the child table.\n key: 'model',\n label: 'Model',\n type: 'enum',\n source: 'usage',\n column: 'model',\n roles: ['chart', 'filter', 'detail'],\n },\n {\n key: 'skill',\n label: 'Skill',\n type: 'string',\n source: 'tool-call',\n column: 'name',\n base: \"action = 'skill'\",\n multi: true,\n roles: ['chart', 'filter', 'detail'],\n },\n {\n // Fingerprinted category of a FAILED tool call (set at ingest by classifyError;\n // NULL when ok). Tool-call grain → splits error_count by category in the Ops\n // \"Errors by category\" widget. `filter`/`detail` only (no `chart`): a session\n // hits MANY categories, so a per-session split explodes into noisy combos, so\n // it is deliberately NOT offered as a success-rate breakdown dimension. Base\n // scopes to errored rows via the column itself (not `is_error = 1`) so the\n // facet stays consistent even if an adapter ever diverges ok from is_error.\n key: 'error_category',\n label: 'Error category',\n type: 'enum',\n source: 'tool-call',\n column: 'error_category',\n base: 'error_category IS NOT NULL',\n roles: ['filter', 'detail'],\n },\n]\n","/**\n * The measure registry: the \"how much\" axis, parallel to the facet registry.\n * A measure is an aggregation (`agg`) of an expression (`expr`) over the\n * population at its grain. Crossed with a facet (the \"which\" axis) by\n * Store.breakdown, it produces every \"<measure> by <facet>\" view.\n *\n * Like facets: intrinsic measures live here; processors add more via\n * Processor.measures; both persist to the `measures` table at analyze time so\n * the serve process discovers them without importing processors.\n *\n * `source` (reused from facets) says WHERE the value lives and implies the grain\n * (grainOf). `expr` is SQL over that source's anchor alias — s (sessions),\n * u (usage_facts), t (tool_calls). For `rate`, expr is a 0/1 (boolean) predicate.\n */\nimport type { FacetSource } from './facets'\nimport type { Grain } from './facets'\n\nexport type MeasureAgg = 'sum' | 'count' | 'count_distinct' | 'avg' | 'rate'\n\nexport interface MeasureSpec {\n key: string\n label?: string\n source: FacetSource\n /** SQL over the anchor alias (s/u/t). For `rate`, a 0/1 boolean expression. */\n expr: string\n agg: MeasureAgg\n /** Optional base predicate restricting the population. */\n base?: string\n format?: 'usd' | 'int' | 'pct'\n}\n\n/** Anchor alias for a grain, used to qualify measure/facet expressions. */\nexport function aliasFor(grain: Grain): string {\n return grain === 'usage' ? 'u' : grain === 'tool_call' ? 't' : grain === 'block' ? 'b' : 's'\n}\n\nexport const INTRINSIC_MEASURES: MeasureSpec[] = [\n // Cost/tokens live at usage grain so they split by model and join up to session facets.\n { key: 'cost', label: 'Cost', source: 'usage', expr: 'u.cost_usd', agg: 'sum', format: 'usd' },\n {\n key: 'tokens',\n label: 'Tokens',\n source: 'usage',\n expr: 'u.tok_input + u.tok_output + u.tok_cache_create + u.tok_cache_read',\n agg: 'sum',\n format: 'int',\n },\n { key: 'sessions', label: 'Sessions', source: 'session', expr: 's.id', agg: 'count_distinct', format: 'int' },\n {\n // Headline: fraction of all sessions with a session_success outcome (full denominator).\n key: 'success_rate',\n label: 'Session Outcome Rate',\n source: 'session',\n expr: \"EXISTS (SELECT 1 FROM outcomes o WHERE o.session_id = s.id AND o.type = 'session_success')\",\n agg: 'rate',\n format: 'pct',\n },\n { key: 'tool_calls', label: 'Tool calls', source: 'tool-call', expr: '1', agg: 'count', format: 'int' },\n { key: 'error_count', label: 'Errors', source: 'tool-call', expr: 't.is_error', agg: 'sum', format: 'int' },\n { key: 'error_rate', label: 'Error rate', source: 'tool-call', expr: 't.is_error', agg: 'rate', format: 'pct' },\n]\n","import { existsSync } from 'node:fs'\nimport { basename, resolve } from 'node:path'\nimport { loadConfig } from '../config'\nimport type { LlmOverrides } from '../config'\nimport { INTRINSIC_FACETS } from '../core/facets'\nimport { INTRINSIC_MEASURES } from '../core/measures'\nimport { assignSeq, NORMALIZE_VERSION } from '../core/blocks'\nimport { mergeSessions, trimInheritedPrefix } from '../core/merge'\nimport type { Session } from '../core/model'\nimport type { SourceAdapter } from '../adapters/types'\nimport { getAdapters, getProcessors } from '../core/registry'\nimport { orderProcessors, runProcessors } from '../core/runner'\nimport { createLlmClient } from '../llm'\nimport { computeSessionCost, priceFor, PRICE_TABLE_VERSION } from '../pricing/pricing'\nimport { loadOpenRouterPrices } from '../pricing/openrouter'\nimport { openDb } from '../store/db'\nimport { Store } from '../store/store'\nimport type { Summary } from '../store/store'\nimport { createLogger } from '../util/log'\nimport { Progress } from '../util/progress'\nimport { makeSh } from '../util/sh'\n\nexport interface AnalyzeOptions {\n dirs?: string[]\n /** `--source` entries: a harness name, optionally `name=dir` to override its roots. */\n sources?: string[]\n db?: string\n verbose?: boolean\n /** Cap the number of sessions processed — handy for a cheap enrichment test. */\n limit?: number\n /** Non-secret LLM flag overrides (provider/model/base-url); the key stays env-only. */\n llm?: LlmOverrides\n}\n\n/**\n * Discover sessions → parse (adapter) → ingest changed ones → run processors\n * (cache-aware) → print a summary. Writes to the store only; the dashboard,\n * `search`, and `observe` all read it.\n */\nexport async function analyze(opts: AnalyzeOptions): Promise<void> {\n const log = createLogger(opts.verbose ? 'debug' : 'info')\n const config = loadConfig({ db: opts.db, llm: opts.llm })\n const db = openDb(config.dbPath)\n const store = new Store(db)\n const sh = makeSh()\n\n // Fetch the OpenRouter price backfill only to price an enrichment model the\n // static table lacks; static-only runs stay offline.\n if (config.llm && !priceFor(config.llm.provider, config.llm.model)) await loadOpenRouterPrices(config.dataDir, log)\n\n const processors = getProcessors()\n store.registerFacets('intrinsic', INTRINSIC_FACETS)\n store.registerMeasures('intrinsic', INTRINSIC_MEASURES)\n for (const p of processors) {\n if (p.facets?.length) store.registerFacets(p.name, p.facets)\n if (p.measures?.length) store.registerMeasures(p.name, p.measures)\n }\n\n let llm = null\n try {\n llm = createLlmClient(config.llm)\n } catch (err) {\n log.warn((err as Error).message)\n }\n const llmEnabled = !!llm\n const llmModel = llm?.model ?? null\n if (llmEnabled) {\n log.info(`LLM enrichment on (${llm!.provider}/${llm!.model}). Session data goes to your configured provider.`)\n } else {\n printEnrichmentHint(log)\n }\n\n let discovered = 0\n let parsed = 0\n let reingested = 0\n\n // Parse every session, then group those that share a logical session (Claude\n // resume/sidechain files, Codex forks) and merge them so each is ingested and\n // processed exactly once.\n const groups = new Map<string, Session[]>()\n const adapters = getAdapters()\n // The stored parse_version is per-adapter (`parseVersion`) composed with the\n // shared NORMALIZE_VERSION, so a per-vendor parser bump re-ingests only that\n // vendor and a normalization bump re-ingests everyone. See ADR-0002.\n const parseVersionBySource = new Map<string, number>()\n for (const a of adapters) parseVersionBySource.set(a.id, a.parseVersion * 1000 + NORMALIZE_VERSION)\n\n // --source name[=dir] (repeatable): pick a subset of harnesses, each optionally\n // with its own roots. No --source → every adapter with its own default. Roots\n // precedence per source: explicit `=dir` ▸ positional [dirs] ▸ defaultRoots().\n const sourceRoots = new Map<string, string[]>()\n const selected = new Set<string>()\n for (const entry of opts.sources ?? []) {\n const eq = entry.indexOf('=')\n const id = resolveSource((eq >= 0 ? entry.slice(0, eq) : entry).trim(), adapters)\n selected.add(id)\n const dir = eq >= 0 ? entry.slice(eq + 1).trim() : ''\n if (dir) sourceRoots.set(id, [...(sourceRoots.get(id) ?? []), dir])\n }\n const activeAdapters = selected.size ? adapters.filter((a) => selected.has(a.id)) : adapters\n\n // Ingest provenance: the (source, directory) roots actually scanned this run,\n // recorded after the run completes so `--schema` / coverage can report what's\n // covered. Only roots that exist on disk — a default root for an uninstalled\n // harness was never really analyzed.\n const scannedRoots: Array<{ source: string; path: string }> = []\n const parsedSessions: Session[] = []\n for (const adapter of activeAdapters) {\n const roots = sourceRoots.get(adapter.id) ?? (opts.dirs && opts.dirs.length > 0 ? opts.dirs : adapter.defaultRoots())\n for (const root of roots) {\n const abs = resolve(root)\n if (existsSync(abs)) scannedRoots.push({ source: adapter.id, path: abs })\n }\n log.debug(`[${adapter.id}] scanning: ${roots.join(', ')}`)\n if (adapter.discoverSessions) {\n // Store-backed adapter (e.g. OpenCode): one DB yields many sessions.\n const sessions = await adapter.discoverSessions(roots)\n discovered += sessions.length\n for (const session of sessions) {\n parsed++\n parsedSessions.push(session)\n }\n } else {\n const files = await adapter.discover(roots)\n discovered += files.length\n for (const file of files) {\n const session = await adapter.parse(file)\n if (!session) continue\n parsed++\n parsedSessions.push(session)\n }\n }\n }\n\n const byId = new Map<string, Session>()\n for (const s of parsedSessions) byId.set(s.id, s)\n\n // A Codex child (`/fork` or sub-agent) replays its parent's transcript prefix —\n // including the parent's token_count usage. Trim that inherited prefix so it's not\n // counted twice; leaves each child with only its own divergent work.\n for (const s of parsedSessions) {\n if (!s.forkedFromId) continue\n const parent = byId.get(`${s.source}:${s.forkedFromId}`)\n if (parent) trimInheritedPrefix(s, parent)\n }\n\n // Group sessions into logical sessions. Same-id sessions merge (Claude resume/sidechain).\n // A sub-agent transcript that lives in its own file (Codex) folds under its ROOT ancestor\n // via `forkedFromId` so the parent merge pulls it in as a sidechain (ADR-0003). A `/fork`\n // also carries `forkedFromId` but is its OWN top-level session (ADR-0005), so only\n // sub-agents fold. Resolve to root in a second pass since a child can be parsed before\n // its parent and nest more than one level deep.\n const rootKey = (s: Session): string => {\n if (!s.isSubagent) return s.id // forks and top-level sessions key on their own id\n const seen = new Set<string>()\n let cur = s\n while (cur.forkedFromId && !seen.has(cur.id)) {\n seen.add(cur.id)\n const parent = byId.get(`${cur.source}:${cur.forkedFromId}`)\n if (!parent) return `${cur.source}:${cur.forkedFromId}` // parent file absent — group under it anyway\n cur = parent\n }\n return cur.id\n }\n for (const s of parsedSessions) {\n const key = rootKey(s)\n const g = groups.get(key)\n if (g) g.push(s)\n else groups.set(key, [s])\n }\n\n // Resolve a session's repo from its cwd via git (the parser only sees cwd, and\n // cwd may be a subdir). Short name = basename of the git toplevel. Cached per\n // cwd since sessions cluster into a few working dirs; null when the dir is gone\n // or isn't a git checkout.\n const repoCache = new Map<string, string | null>()\n const resolveRepo = async (cwd: string | undefined): Promise<string | null> => {\n if (!cwd) return null\n const cached = repoCache.get(cwd)\n if (cached !== undefined) return cached\n let repo: string | null = null\n const res = await sh('git', ['-C', cwd, 'rev-parse', '--show-toplevel'])\n if (res && res.code === 0) {\n const top = res.stdout.trim()\n if (top) repo = basename(top)\n }\n repoCache.set(cwd, repo)\n return repo\n }\n\n // Merge each group, then process oldest-first. Chronological order matters:\n // enrich-session builds the feature taxonomy incrementally — each session links\n // to features proposed by sessions processed before it — so the earliest session\n // to touch a feature should be the one that creates it. Map iteration order is\n // discovery order (filesystem walk / per-adapter), not time, so sort by\n // startedAt. Sessions with no startedAt sort first; ties keep a stable order.\n const merged = [...groups.values()].map((group) => mergeSessions(group))\n merged.sort((a, b) => (a.startedAt ?? '').localeCompare(b.startedAt ?? ''))\n\n // Pre-scan: count sessions that will need processing (at least one cache miss).\n const sessionsToProcess = opts.limit != null ? merged.slice(0, opts.limit) : merged\n const ordered = orderProcessors(processors)\n let totalNeedingWork = 0\n for (const session of sessionsToProcess) {\n const prior = store.storedMeta(session.id)\n const parseVersion = parseVersionBySource.get(session.source) ?? NORMALIZE_VERSION\n if (prior?.hash !== session.raw.contentHash || prior.parseVersion < parseVersion) {\n totalNeedingWork++\n continue\n }\n const inputHash = session.raw.contentHash\n let anyMiss = false\n for (const p of ordered) {\n if (p.needs?.llm && !llmEnabled) continue\n const model = p.needs?.llm ? llmModel : null\n const cached = store.processorRun(session.id, p.name)\n if (!cached || cached.version !== p.version || cached.inputHash !== inputHash || cached.model !== model) {\n anyMiss = true\n break\n }\n }\n if (anyMiss) totalNeedingWork++\n }\n const n = sessionsToProcess.length\n log.info(\n `${n} ${n === 1 ? 'session' : 'sessions'} to scan, ` +\n `${totalNeedingWork} ${totalNeedingWork === 1 ? 'needs' : 'need'} processing.`,\n )\n\n const progress = new Progress(sessionsToProcess.length, totalNeedingWork)\n\n let processed = 0\n for (const session of merged) {\n if (opts.limit != null && processed >= opts.limit) break\n assignSeq(session) // main-thread seq, post-merge — the block partition's coordinate\n const repo = await resolveRepo(session.project.cwd)\n if (repo) session.project.repo = repo\n const prior = store.storedMeta(session.id)\n const parseVersion = parseVersionBySource.get(session.source) ?? NORMALIZE_VERSION\n const needsIngest = prior?.hash !== session.raw.contentHash || prior.parseVersion < parseVersion\n // Re-ingest when the transcript changed OR a newer parser can extract more\n // from the same bytes (e.g. skill names — the adapter's parseVersion bumped).\n if (needsIngest) {\n const cost = computeSessionCost(session)\n store.ingestSession(session, cost.usd, cost.facts, PRICE_TABLE_VERSION, parseVersion)\n if (cost.unpriced.length > 0) {\n log.debug(`unpriced model(s) in ${session.id}: ${cost.unpriced.join(', ')}`)\n }\n reingested++\n }\n // Backfill repo onto already-ingested (unchanged) sessions too, without a\n // version bump. Populate-only: never overwrite a known repo with null.\n if (repo) store.setSessionRepo(session.id, repo)\n\n const t0 = Date.now()\n const { costUsd: sessionCost } = await runProcessors({ session, processors, store, log, llmEnabled, llmModel, llm, sh })\n const elapsedMs = Date.now() - t0\n\n const didWork = elapsedMs > 50 || sessionCost > 0\n progress.tick(didWork, elapsedMs, sessionCost)\n processed++\n }\n progress.clear()\n\n // Refresh stale artifacts: let each processor with a refresh() method re-check\n // its unresolved artifacts against the live source (e.g. open PRs → gh).\n for (const p of processors) {\n if (!p.refresh) continue\n const stale = store.unresolvedArtifacts(p.name)\n if (!stale.length) continue\n log.debug(`refreshing ${stale.length} unresolved artifact(s) for ${p.name}`)\n try {\n const result = await p.refresh({ artifacts: stale, log, sh })\n if (result.artifacts?.length || result.outcomes?.length) {\n store.persistRefresh(p.name, result)\n log.info(`${p.name}: refreshed ${result.artifacts?.length ?? 0} artifact(s)`)\n }\n } catch (err) {\n log.warn(`refresh failed for ${p.name}: ${(err as Error).message}`)\n }\n }\n\n // Remove sessions that the parser now skips (returns null) — their parse_version\n // stays stale since they were never re-ingested this run.\n const staleSessionCount = store.pruneStaleSessionsByVersion(parseVersionBySource)\n if (staleSessionCount > 0) log.debug(`pruned ${staleSessionCount} stale session(s)`)\n\n const pruned = store.pruneOrphanArtifacts()\n if (pruned > 0) log.debug(`pruned ${pruned} orphan artifact(s)`)\n\n log.info(\n `Scanned ${discovered} file(s), parsed ${parsed} session(s) into ${groups.size} unique session(s), ${reingested} new/changed.`,\n )\n // Stamp completion at the very end so a run that crashed partway can't claim the\n // store is fresh. Drives the dashboard's \"last analyzed\" line + the stale-store\n // nudge; per-session analyzed_at / processor ran_at only move when work is done,\n // so they can't answer \"when did analyze last finish\" (e.g. for a no-op re-run).\n const finishedAt = new Date().toISOString()\n store.setMeta('last_analyze_at', finishedAt)\n // Per-directory provenance, stamped with the same completion time.\n store.recordAnalyzedRoots(scannedRoots, finishedAt)\n printSummary(store.summary())\n store.close()\n}\n\n/**\n * Notice when no enrichment provider is configured — prints whenever enrichment\n * is off (not just the first run). Discoverability, not a gate; the multi-line\n * form with setup hints prints only on an interactive terminal.\n */\nfunction printEnrichmentHint(log: ReturnType<typeof createLogger>): void {\n if (!process.stdout.isTTY) {\n log.info('LLM enrichment off (set TUNELOOP_LLM_PROVIDER + key to enable). Static analysis only.')\n return\n }\n process.stdout.write(\n [\n '',\n 'LLM enrichment is off — static analysis only. Enable it with your own key, e.g.:',\n ' export TUNELOOP_LLM_PROVIDER=openrouter',\n ' export OPENROUTER_API_KEY=sk-or-...',\n ' Providers: anthropic, openai, openrouter, groq, deepseek, gemini, ollama (see README).',\n '',\n ].join('\\n') + '\\n',\n )\n}\n\n/** Resolve a --source name to a registered adapter id, tolerant of a short alias (`claude` → `claude-code`). */\nfunction resolveSource(name: string, adapters: SourceAdapter[]): string {\n const exact = adapters.find((a) => a.id === name)\n if (exact) return exact.id\n const prefix = adapters.filter((a) => a.id.startsWith(name))\n if (prefix.length === 1) return prefix[0]!.id\n const available = adapters.map((a) => a.id).join(', ')\n if (prefix.length > 1) {\n throw new Error(`ambiguous --source \"${name}\" (matches ${prefix.map((a) => a.id).join(', ')})`)\n }\n throw new Error(`unknown --source \"${name}\" (available: ${available})`)\n}\n\nfunction printSummary(s: Summary): void {\n const usd = (n: number) => `$${n.toFixed(2)}`\n const out: string[] = []\n out.push('')\n out.push(' Sessions ' + s.sessions)\n out.push(' Total spend ' + usd(s.costUsd))\n out.push(' Tokens ' + s.tokens.toLocaleString('en-US'))\n if (s.firstAt && s.lastAt) out.push(` Range ${s.firstAt.slice(0, 10)} → ${s.lastAt.slice(0, 10)}`)\n\n if (s.costPerMergedPr.costPerUnit != null) {\n out.push(` Cost / merged PR ${usd(s.costPerMergedPr.costPerUnit)} (${s.costPerMergedPr.count} merged)`)\n }\n if (s.analysisCostUsd > 0) out.push(' Analysis spend ' + usd(s.analysisCostUsd) + ' (enrichment)')\n\n out.push('')\n process.stdout.write(out.join('\\n') + '\\n')\n}\n","import { contentHash } from './hash'\nimport { addUsage, emptyUsage } from './model'\nimport type { AssistantMessage, Event, Session, SubagentMeta, TokenUsage, ToolCall } from './model'\n\n/**\n * Trim the parent transcript prefix that a Codex child (`/fork` or sub-agent) replays\n * into its own file — including the parent's `token_count` usage. Without\n * this the inherited prefix is counted twice: a fork sums into the parent, and a\n * sub-agent folds its full token total in. The boundary is the leading run of assistant\n * messages whose per-call usage matches the parent's (the inherited stamps are verbatim\n * copies); everything from divergence on is the child's own work. Tool calls are trimmed\n * by the matching leading run of call ids (forks replay them; sub-agents don't). Mutates\n * `child` in place; no-op when nothing is shared (e.g. parent file absent).\n */\nexport function trimInheritedPrefix(child: Session, parent: Session): void {\n // Match on the real token_count stamps (non-zero usage) only. Content-only ZERO\n // flushes (message boundaries) interleave differently between a sub-agent and its\n // parent, so aligning every assistant message misaligns; the usage stamps don't.\n const stamps = (s: Session): AssistantMessage[] =>\n s.events.filter((e): e is AssistantMessage => e.kind === 'assistant' && !isZeroUsage(e.usage))\n const childS = stamps(child)\n const parentS = stamps(parent)\n let k = 0\n while (k < childS.length && k < parentS.length && usageEq(childS[k]!.usage, parentS[k]!.usage)) k++\n if (k === 0) return // nothing inherited\n\n // Drop every event up to and including the last inherited usage stamp.\n const boundary = child.events.indexOf(childS[k - 1]!)\n child.events = child.events.slice(boundary + 1)\n\n // Drop the leading tool calls the child shares with the parent (by call id).\n let kc = 0\n while (\n kc < child.toolCalls.length &&\n kc < parent.toolCalls.length &&\n child.toolCalls[kc]!.id === parent.toolCalls[kc]!.id\n ) {\n kc++\n }\n child.toolCalls = child.toolCalls.slice(kc)\n\n // Re-roll the token total from the kept assistant messages.\n let tokens = emptyUsage()\n for (const e of child.events) if (e.kind === 'assistant') tokens = addUsage(tokens, e.usage)\n child.tokens = tokens\n}\n\nfunction usageEq(a: TokenUsage, b: TokenUsage): boolean {\n return a.input === b.input && a.output === b.output && a.cacheCreate === b.cacheCreate && a.cacheRead === b.cacheRead\n}\n\nfunction isZeroUsage(u: TokenUsage): boolean {\n return u.input === 0 && u.output === 0 && u.cacheCreate === 0 && u.cacheRead === 0\n}\n\n/**\n * Merge files that make up one logical session into a single session. A session\n * splits across files when Claude Code resumes or writes sidechain/subagent\n * transcripts tagged with the parent id, or when Codex writes each sub-agent to\n * its own rollout file (`forkedFromId` → parent). Processing them separately would\n * re-run processors on the same id and clobber per-session rows. Merging rolls\n * everything — including sidechain tokens and tool calls — into one session,\n * processed once. The base is the top-level member (no `forkedFromId`); sub-agent\n * members contribute their already-sidechain events, token rollup, and SubagentMeta.\n */\nexport function mergeSessions(group: Session[]): Session {\n // A merged session is always the ROOT of its group, so it never carries a\n // forkedFromId. Clearing it on a lone member matters for an orphan sub-agent\n // (parent file missing): it becomes a clean standalone session rather than a\n // child of an absent parent. (It keeps 0 blocks — its events stay sidechain —\n // but cost still counts at session grain.)\n if (group.length === 1) {\n const s = group[0]!\n return s.forkedFromId ? { ...s, forkedFromId: undefined } : s\n }\n const sorted = [...group].sort((a, b) => (a.startedAt ?? '').localeCompare(b.startedAt ?? ''))\n // The parent (top-level, no forkedFromId) supplies identity/project; fall back to\n // earliest if the group is all sidechains (parent file missing).\n const first = sorted.find((s) => !s.forkedFromId) ?? sorted[0]!\n\n const models = new Set<string>()\n let tokens = emptyUsage()\n const events: Event[] = []\n const toolCalls: ToolCall[] = []\n // Subagents come from the per-sidechain files; merging gathers them all onto\n // the one logical session (deduped by agentId).\n const subagents = new Map<string, SubagentMeta>()\n let title: string | undefined\n let cwd: string | undefined\n let branch: string | undefined\n let repo: string | undefined\n let startedAt: string | undefined\n let endedAt: string | undefined\n\n for (const s of sorted) {\n for (const m of s.models) models.add(m)\n tokens = addUsage(tokens, s.tokens)\n events.push(...s.events)\n toolCalls.push(...s.toolCalls)\n for (const sa of s.subagents ?? []) if (!subagents.has(sa.agentId)) subagents.set(sa.agentId, sa)\n title ??= s.title\n cwd ??= s.project.cwd\n branch ??= s.project.branch\n repo ??= s.project.repo\n if (s.startedAt && (!startedAt || s.startedAt < startedAt)) startedAt = s.startedAt\n if (s.endedAt && (!endedAt || s.endedAt > endedAt)) endedAt = s.endedAt\n }\n\n // Link each sub-agent to the tool call that spawned it: the task_spawn call whose\n // result references the sub-agent's id (Codex's spawn_agent returns `{agent_id}`).\n // This is what the block-attribution rollup keys on. Claude sets toolUseId at parse,\n // so the guard skips already-linked sub-agents\n for (const sa of subagents.values()) {\n if (sa.toolUseId) continue\n const spawn = toolCalls.find(\n (t) => t.action === 'task_spawn' && typeof t.result.raw === 'string' && t.result.raw.includes(sa.agentId),\n )\n if (spawn) sa.toolUseId = spawn.id\n }\n\n return {\n ...first,\n forkedFromId: undefined, // the merged session is the root of its group\n title,\n project: { cwd, branch, repo },\n startedAt,\n endedAt,\n models: [...models],\n tokens,\n events,\n toolCalls,\n subagents: subagents.size ? [...subagents.values()] : undefined,\n // Hash over all member files so the cache invalidates when any of them change.\n raw: { path: first.raw.path, contentHash: contentHash(sorted.map((s) => s.raw.contentHash).join(':')) },\n }\n}\n","import { mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport Database from 'better-sqlite3'\n\nexport type DB = Database.Database\n\nconst SCHEMA_VERSION = 9\n\n/**\n * The store is fact tables only — no pre-aggregated metrics. Every dashboard\n * number is a query over these (see ARCHITECTURE.md). `producer` columns let a\n * processor's rows be replaced on re-run without touching user-authored rows.\n */\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT\n);\n\n-- Ingest provenance: which source directories were scanned, and when each was\n-- last analyzed. Upserted per run; a root untouched by a scoped re-run (e.g.\n-- \\`--source codex\\`) keeps its prior timestamp, so this answers \"when was THIS\n-- directory last analyzed\" per directory, unlike the store-wide meta.last_analyze_at.\nCREATE TABLE IF NOT EXISTS analyzed_roots (\n source TEXT,\n path TEXT,\n last_analyzed_at TEXT,\n PRIMARY KEY (source, path)\n);\n\n-- Hot row per session. Aggregation queries live here; the blob is elsewhere.\nCREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n session_id TEXT,\n source TEXT,\n provider TEXT,\n title TEXT,\n repo TEXT,\n branch TEXT,\n cwd TEXT,\n started_at TEXT,\n ended_at TEXT,\n n_turns INTEGER,\n n_tool_calls INTEGER,\n models TEXT, -- json array\n tok_input INTEGER,\n tok_output INTEGER,\n tok_cache_create INTEGER,\n tok_cache_read INTEGER,\n cost_usd REAL,\n price_table_version TEXT,\n content_hash TEXT,\n parse_version INTEGER,\n analyzed_at TEXT\n);\nCREATE INDEX IF NOT EXISTS ix_sessions_started ON sessions(started_at);\nCREATE INDEX IF NOT EXISTS ix_sessions_repo ON sessions(repo);\n\n-- Normalized session JSON (gzipped) for the transcript viewer. Self-contained:\n-- survives Claude Code rotating the original .jsonl files.\nCREATE TABLE IF NOT EXISTS session_blobs (\n id TEXT PRIMARY KEY,\n gz BLOB,\n FOREIGN KEY(id) REFERENCES sessions(id) ON DELETE CASCADE\n);\n\n-- Relational backbone: tool/skill usage, error rates, files-touched all read here.\nCREATE TABLE IF NOT EXISTS tool_calls (\n session_id TEXT,\n idx INTEGER,\n name TEXT,\n action TEXT,\n ok INTEGER,\n is_error INTEGER,\n error_category TEXT,\n error_message TEXT,\n target_path TEXT,\n command TEXT,\n is_sidechain INTEGER,\n ts TEXT,\n duration_ms INTEGER,\n PRIMARY KEY (session_id, idx),\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS ix_tool_calls_name ON tool_calls(name);\nCREATE INDEX IF NOT EXISTS ix_tool_calls_action ON tool_calls(action);\nCREATE INDEX IF NOT EXISTS ix_tool_calls_error_category ON tool_calls(error_category);\n\n-- Per-assistant-message usage facts: the atomic grain of token economics.\n-- Model / main-vs-sidechain / time are dimension columns, so every usage\n-- breakdown is a read-time GROUP BY (summing cost by model off sessions.models\n-- would double-count). Rebuilt wholesale on re-ingest, like tool_calls.\nCREATE TABLE IF NOT EXISTS usage_facts (\n session_id TEXT,\n idx INTEGER,\n model TEXT,\n is_sidechain INTEGER,\n ts TEXT,\n tok_input INTEGER,\n tok_output INTEGER,\n tok_cache_create INTEGER,\n tok_cache_read INTEGER,\n cost_usd REAL,\n PRIMARY KEY (session_id, idx),\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS ix_usage_facts_model ON usage_facts(model);\n\n-- Polymorphic artifacts (file | commit | pr | ticket | feature) with completion.\nCREATE TABLE IF NOT EXISTS artifacts (\n id TEXT PRIMARY KEY,\n kind TEXT,\n repo TEXT,\n ident TEXT,\n external_id TEXT,\n source TEXT,\n title TEXT,\n owner TEXT,\n complexity REAL,\n complexity_basis TEXT,\n status TEXT,\n created_at TEXT,\n completed_at TEXT,\n parent_artifact_id TEXT,\n json TEXT,\n producer TEXT\n);\nCREATE INDEX IF NOT EXISTS ix_artifacts_kind_completed ON artifacts(kind, completed_at);\n\n-- Artifact -> artifact edges (the transitive chain to features).\nCREATE TABLE IF NOT EXISTS artifact_links (\n from_id TEXT,\n to_id TEXT,\n relation TEXT,\n source TEXT,\n confidence REAL,\n producer TEXT,\n PRIMARY KEY (from_id, to_id, relation)\n);\n\nCREATE TABLE IF NOT EXISTS session_artifacts (\n session_id TEXT,\n artifact_id TEXT,\n role TEXT,\n source TEXT,\n confidence REAL,\n producer TEXT,\n PRIMARY KEY (session_id, artifact_id, role),\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS ix_session_artifacts_artifact ON session_artifacts(artifact_id);\n\n-- Multi-tag per session. artifact_id is nullable (session_success, plan_drafted, ...).\nCREATE TABLE IF NOT EXISTS outcomes (\n session_id TEXT,\n type TEXT,\n artifact_id TEXT,\n ts TEXT,\n producer TEXT,\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS ix_outcomes_session ON outcomes(session_id, type);\nCREATE INDEX IF NOT EXISTS ix_outcomes_type ON outcomes(type);\n\n-- Generic facts. Every enrichment + custom dimension lands here; no migration\n-- needed to add a processor.\nCREATE TABLE IF NOT EXISTS annotations (\n session_id TEXT,\n processor TEXT,\n key TEXT,\n value TEXT, -- json\n PRIMARY KEY (session_id, processor, key),\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS ix_annotations_key ON annotations(session_id, key);\n\n-- Cache + provenance + \"cost of running the analysis itself\".\nCREATE TABLE IF NOT EXISTS processor_runs (\n session_id TEXT,\n processor TEXT,\n version INTEGER,\n input_hash TEXT,\n model TEXT,\n status TEXT,\n in_tokens INTEGER,\n out_tokens INTEGER,\n cost_usd REAL,\n ran_at TEXT,\n -- Set to 1 by a user link/unlink to force the next analyze to re-run this\n -- processor; reset to 0 (the default) whenever persistResult rewrites the row.\n invalidated INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (session_id, processor)\n);\n\n-- file -> session index for \\`tuneloop search <repo:file>\\`.\nCREATE TABLE IF NOT EXISTS files_index (\n repo TEXT,\n path TEXT,\n session_id TEXT,\n producer TEXT,\n PRIMARY KEY (repo, path, session_id)\n);\nCREATE INDEX IF NOT EXISTS ix_files_index_path ON files_index(path);\n\n-- Facet registry: the single source of truth for chartable/filterable dimensions.\n-- Populated each analyze from intrinsic facets + processor-declared facets, so the\n-- serve process discovers them without importing processors. The source/multi/col/\n-- base columns are what the generic query builder needs (see Store.facetDistribution).\nCREATE TABLE IF NOT EXISTS facets (\n key TEXT PRIMARY KEY,\n label TEXT,\n type TEXT,\n source TEXT,\n col TEXT,\n base TEXT,\n multi INTEGER,\n roles TEXT, -- json array of 'chart' | 'filter' | 'detail'\n producer TEXT\n);\n\n-- Measure registry: the \"how much\" axis. Crossed with facets by Store.breakdown.\n-- expr is SQL over the source's anchor alias; agg is how to combine it.\nCREATE TABLE IF NOT EXISTS measures (\n key TEXT PRIMARY KEY,\n label TEXT,\n source TEXT,\n expr TEXT,\n agg TEXT,\n base TEXT,\n format TEXT,\n producer TEXT\n);\n\n-- Block-level attribution (handling_long_sessions). A block is a contiguous,\n-- deterministic slice of a session's MAIN thread; the membership join tables map\n-- each usage_facts / tool_calls row to its block so cost attributes at block grain.\n-- Owned by the segment-blocks processor; block_annotations/block_artifacts are\n-- layered on by enrich-session (use_case, feature) and outcomes-git (PR/commit).\nCREATE TABLE IF NOT EXISTS blocks (\n session_id TEXT,\n idx INTEGER, -- 0-based main-thread block ordinal\n start_seq INTEGER, -- inclusive main-thread seq\n end_seq INTEGER, -- inclusive\n boundary_kind TEXT, -- 'user_turn' | 'commit' | 'pr_create' | 'pr_merge' | 'session_end'\n ts_start TEXT,\n ts_end TEXT,\n producer TEXT,\n PRIMARY KEY (session_id, idx),\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS ix_blocks_session ON blocks(session_id);\n\n-- usage_facts row -> its block. PK (session_id, usage_idx) enforces that a usage\n-- row belongs to exactly one block (non-overlap); exhaustiveness is asserted.\nCREATE TABLE IF NOT EXISTS block_usage (\n session_id TEXT,\n usage_idx INTEGER, -- usage_facts.idx\n block_idx INTEGER,\n producer TEXT,\n PRIMARY KEY (session_id, usage_idx),\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS ix_block_usage_block ON block_usage(session_id, block_idx);\n\nCREATE TABLE IF NOT EXISTS block_tool (\n session_id TEXT,\n tool_idx INTEGER, -- tool_calls.idx\n block_idx INTEGER,\n producer TEXT,\n PRIMARY KEY (session_id, tool_idx),\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS ix_block_tool_block ON block_tool(session_id, block_idx);\n\n-- Per-block labels (parallels annotations). use_case lands here.\nCREATE TABLE IF NOT EXISTS block_annotations (\n session_id TEXT,\n block_idx INTEGER,\n processor TEXT,\n key TEXT,\n value TEXT, -- json\n PRIMARY KEY (session_id, block_idx, processor, key),\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\n\n-- block -> artifact (PR/commit deterministic from outcomes-git; feature from enrich-session).\nCREATE TABLE IF NOT EXISTS block_artifacts (\n session_id TEXT,\n block_idx INTEGER,\n artifact_id TEXT,\n role TEXT,\n source TEXT,\n confidence REAL,\n producer TEXT,\n PRIMARY KEY (session_id, block_idx, artifact_id, role),\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS ix_block_artifacts_artifact ON block_artifacts(artifact_id);\n\n-- User overrides for session→artifact links (e.g. rejecting a derived feature link).\n-- Processors check this before inserting derived links to respect user decisions.\nCREATE TABLE IF NOT EXISTS user_link_overrides (\n session_id TEXT,\n artifact_id TEXT,\n action TEXT, -- 'reject'\n created_at TEXT,\n PRIMARY KEY (session_id, artifact_id),\n FOREIGN KEY(session_id) REFERENCES sessions(id) ON DELETE CASCADE\n);\n`\n\nexport function openDb(path: string): DB {\n mkdirSync(dirname(path), { recursive: true })\n const db = new Database(path)\n db.pragma('journal_mode = WAL')\n db.pragma('foreign_keys = ON')\n migrate(db) // add columns to pre-existing tables before SCHEMA (its indexes reference them)\n db.exec(SCHEMA)\n db.prepare('INSERT OR REPLACE INTO meta(key, value) VALUES (?, ?)').run(\n 'schema_version',\n String(SCHEMA_VERSION),\n )\n return db\n}\n\n/**\n * Add columns that `CREATE TABLE IF NOT EXISTS` can't retrofit onto an existing\n * table. Runs before SCHEMA so the schema's indexes on the new column succeed.\n * A pragma on a not-yet-created table returns nothing, so fresh DBs skip these\n * (SCHEMA creates the column directly) — keeping each step idempotent.\n */\nfunction migrate(db: DB): void {\n const has = (table: string, col: string): boolean => {\n const cols = db.prepare(`SELECT name FROM pragma_table_info(?)`).all(table) as Array<{ name: string }>\n return cols.length > 0 && cols.some((c) => c.name === col)\n }\n const tableExists = (table: string) => (db.prepare(`SELECT name FROM pragma_table_info(?)`).all(table) as unknown[]).length > 0\n if (tableExists('tool_calls') && !has('tool_calls', 'error_category')) {\n db.exec('ALTER TABLE tool_calls ADD COLUMN error_category TEXT')\n }\n if (tableExists('tool_calls') && !has('tool_calls', 'error_message')) {\n db.exec('ALTER TABLE tool_calls ADD COLUMN error_message TEXT')\n }\n if (tableExists('processor_runs') && !has('processor_runs', 'invalidated')) {\n db.exec('ALTER TABLE processor_runs ADD COLUMN invalidated INTEGER NOT NULL DEFAULT 0')\n }\n}\n","/* Minimal leveled logger. Quiet by default; -v / TUNELOOP_DEBUG raises verbosity. */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nconst ORDER: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 }\n\nexport interface Logger {\n debug(msg: string): void\n info(msg: string): void\n warn(msg: string): void\n error(msg: string): void\n}\n\nexport function createLogger(level: LogLevel = 'info'): Logger {\n const min = ORDER[level]\n const emit = (lvl: LogLevel, msg: string) => {\n if (ORDER[lvl] < min) return\n const stream = lvl === 'error' || lvl === 'warn' ? process.stderr : process.stdout\n stream.write(`${msg}\\n`)\n }\n return {\n debug: (m) => emit('debug', ` ${m}`),\n info: (m) => emit('info', m),\n warn: (m) => emit('warn', `warning: ${m}`),\n error: (m) => emit('error', `error: ${m}`),\n }\n}\n","export interface ProgressState {\n current: number\n total: number\n needingWork: number\n worked: number\n avgMs: number | null\n costUsd: number\n}\n\nexport class Progress {\n private state: ProgressState\n private stream: NodeJS.WriteStream\n\n constructor(total: number, needingWork: number, stream: NodeJS.WriteStream = process.stderr) {\n this.stream = stream\n this.state = { current: 0, total, needingWork, worked: 0, avgMs: null, costUsd: 0 }\n }\n\n tick(didWork: boolean, elapsedMs: number, costUsd: number) {\n this.state.current++\n this.state.costUsd += costUsd\n if (didWork) {\n this.state.worked++\n this.state.avgMs =\n this.state.avgMs == null\n ? elapsedMs\n : this.state.avgMs + (elapsedMs - this.state.avgMs) / this.state.worked\n }\n this.render()\n }\n\n clear() {\n if (!this.stream.isTTY) return\n this.stream.clearLine(0)\n this.stream.cursorTo(0)\n }\n\n private render() {\n if (!this.stream.isTTY) return\n const { current, total, needingWork, worked, avgMs, costUsd } = this.state\n const pct = Math.round((current / total) * 100)\n const barLen = 20\n const filled = Math.round((current / total) * barLen)\n const bar = '█'.repeat(filled) + '░'.repeat(barLen - filled)\n\n let line = ` [${bar}] ${current}/${total} (${pct}%)`\n\n if (avgMs != null) {\n const avgSec = (avgMs / 1000).toFixed(1)\n const remaining = needingWork - worked\n const etaMs = remaining * avgMs\n line += ` | ${avgSec}s/session | ETA: ${formatDuration(etaMs)}`\n }\n\n if (costUsd > 0) {\n const remaining = needingWork - worked\n const avgCost = costUsd / worked\n const estTotal = costUsd + remaining * avgCost\n line += ` | Cost: $${costUsd.toFixed(4)} (est. total $${estTotal.toFixed(2)})`\n }\n\n this.stream.clearLine(0)\n this.stream.cursorTo(0)\n this.stream.write(line)\n }\n}\n\nfunction formatDuration(ms: number): string {\n const secs = Math.round(ms / 1000)\n if (secs < 60) return `${secs}s`\n const mins = Math.floor(secs / 60)\n const rem = secs % 60\n if (mins < 60) return `${mins}m ${rem}s`\n const hrs = Math.floor(mins / 60)\n return `${hrs}h ${mins % 60}m`\n}\n","import { execFile } from 'node:child_process'\nimport type { ShResult } from '../core/processor'\n\n/**\n * Run a local binary, resolving null when it's missing (ENOENT) so processors\n * that shell out to `git`/`gh` degrade gracefully offline or uninstalled.\n */\nexport function makeSh() {\n return (cmd: string, args: string[], opts?: { cwd?: string }): Promise<ShResult | null> =>\n new Promise((resolvePromise) => {\n execFile(\n cmd,\n args,\n { cwd: opts?.cwd, timeout: 20_000, maxBuffer: 16 * 1024 * 1024 },\n (err, stdout, stderr) => {\n const e = err as (NodeJS.ErrnoException & { code?: number | string }) | null\n if (e && e.code === 'ENOENT') return resolvePromise(null)\n const code = typeof e?.code === 'number' ? e.code : e ? 1 : 0\n resolvePromise({ stdout: stdout?.toString() ?? stderr?.toString() ?? '', code })\n },\n )\n })\n}\n","import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http'\nimport { readFile } from 'node:fs/promises'\nimport { existsSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\nimport type { Bucket, Store } from '../store/store'\nimport type { ShResult } from '../core/processor'\nimport { ERROR_CATEGORIES } from '../core/error-category'\n\nexport type ShFn = (cmd: string, args: string[]) => Promise<ShResult | null>\n\n/**\n * JSON API + dashboard SPA over the analyzed store. Reads are queries at request\n * time; POST endpoints write user curation only (features + session↔artifact\n * links), stamped user-authored so `analyze` never clobbers them. Deriving facts\n * from transcripts stays in the `analyze` path.\n */\nexport function createDashboardServer(store: Store, dbPath: string, sh?: ShFn): Server {\n return createServer((req, res) => {\n route(req, res, store, dbPath, sh ?? null).catch((err) => sendJson(res, 500, { error: (err as Error).message }))\n })\n}\n\nasync function route(req: IncomingMessage, res: ServerResponse, store: Store, dbPath: string, sh: ShFn | null): Promise<void> {\n const url = new URL(req.url ?? '/', 'http://localhost')\n const path = url.pathname\n\n if (req.method === 'POST') {\n const body = await readBody(req)\n if (path === '/api/features') {\n const title = String(body.title ?? '').trim()\n if (!title) {\n sendJson(res, 400, { error: 'title required' })\n return\n }\n const complexity = body.complexity != null ? Number(body.complexity) : undefined\n sendJson(res, 200, store.createFeature(title, body.parentId || undefined, Number.isFinite(complexity) ? complexity : undefined))\n return\n }\n if (path === '/api/features/update') {\n if (body.complexity !== undefined) {\n body.complexity = body.complexity != null ? Number(body.complexity) : null\n if (typeof body.complexity === 'number' && !Number.isFinite(body.complexity)) body.complexity = null\n }\n sendJson(res, 200, { ok: store.updateFeature(String(body.id), body) })\n return\n }\n if (path === '/api/features/delete') {\n sendJson(res, 200, { ok: store.deleteFeature(String(body.id)) })\n return\n }\n if (path === '/api/session-links/add') {\n const { sessionId, artifactId, role } = body\n if (!sessionId || !artifactId) {\n sendJson(res, 400, { error: 'sessionId and artifactId required' })\n return\n }\n const ok = store.addSessionLink(sessionId, artifactId, role || 'contributed')\n sendJson(res, ok ? 200 : 404, { ok })\n return\n }\n if (path === '/api/session-links/create-feature') {\n const { sessionId, title, parentId } = body\n if (!sessionId || !String(title ?? '').trim()) {\n sendJson(res, 400, { error: 'sessionId and title required' })\n return\n }\n const result = store.createAndLinkFeature(sessionId, String(title).trim(), parentId || undefined)\n sendJson(res, result ? 200 : 404, result ?? { error: 'session not found' })\n return\n }\n if (path === '/api/session-links/add-pr') {\n const { sessionId, repo, prNumber } = body\n if (!sessionId || !repo || !prNumber || !/^\\d+$/.test(String(prNumber))) {\n sendJson(res, 400, { error: 'sessionId, repo, and a numeric prNumber required' })\n return\n }\n const prRef = `${repo}#${prNumber}`\n let title: string | undefined\n let status: string | undefined\n let externalId: string | undefined\n let warning: string | undefined\n if (sh) {\n const ghRes = await sh('gh', ['pr', 'view', String(prNumber), '--repo', repo, '--json', 'title,state,url'])\n if (ghRes === null) {\n warning = 'gh CLI not available — PR not validated'\n } else if (ghRes.code !== 0) {\n const detail = ghRes.stdout.trim().split('\\n')[0] || 'not found'\n sendJson(res, 404, { error: `PR ${prRef}: ${detail}` })\n return\n } else {\n try {\n const pr = JSON.parse(ghRes.stdout) as { title?: string; state?: string; url?: string }\n title = pr.title ?? undefined\n status = pr.state?.toLowerCase() ?? undefined\n externalId = pr.url ?? undefined\n } catch { /* parse failed — proceed without metadata */ }\n }\n }\n const result = store.upsertAndLinkPr(sessionId, repo, String(prNumber), { title, status, externalId })\n if (!result) { sendJson(res, 404, { error: 'session not found' }); return }\n sendJson(res, 200, { ...result, warning })\n return\n }\n if (path === '/api/session-links/remove') {\n const { sessionId, artifactId } = body\n if (!sessionId || !artifactId) {\n sendJson(res, 400, { error: 'sessionId and artifactId required' })\n return\n }\n const ok = store.rejectSessionLink(sessionId, artifactId)\n sendJson(res, ok ? 200 : 404, { ok })\n return\n }\n sendJson(res, 404, { error: 'not found' })\n return\n }\n\n if (path === '/' || path === '/index.html') {\n await sendFile(res, join(clientDir(), 'index.html'), 'text/html; charset=utf-8')\n return\n }\n if (path.startsWith('/client/')) {\n await serveClientAsset(res, path)\n return\n }\n if (path === '/api/highlights') {\n const daysRaw = url.searchParams.get('days')\n const days = daysRaw === 'all' ? null : parseInt(daysRaw ?? '7', 10)\n // The store windows on a half-open [from, to) range — both bounds required.\n const windowed = !!(days && days > 0)\n const to = windowed ? new Date().toISOString() : undefined\n const from = windowed ? new Date(Date.now() - days! * 86400000).toISOString() : undefined\n sendJson(res, 200, { days: daysRaw ?? '7', highlights: store.highlights(from, to), dbPath })\n return\n }\n\n if (path === '/api/overview') {\n sendJson(res, 200, { ...store.summary(), dbPath })\n return\n }\n if (path === '/api/facets') {\n sendJson(res, 200, store.facetList())\n return\n }\n if (path === '/api/error-categories') {\n // Taxonomy metadata (labels + tooltip descriptions) for the error-category widget.\n sendJson(res, 200, ERROR_CATEGORIES)\n return\n }\n if (path === '/api/error-occurrences') {\n // Drill-down: every failed tool call of one category, windowed, for the widget.\n const category = url.searchParams.get('category')\n if (!category) {\n sendJson(res, 400, { error: 'missing category' })\n return\n }\n const window = { from: url.searchParams.get('from') ?? undefined, to: url.searchParams.get('to') ?? undefined }\n const toolNames = url.searchParams.getAll('tool_name').filter(Boolean)\n sendJson(res, 200, store.errorOccurrences(category, window, toolNames))\n return\n }\n if (path === '/api/distribution') {\n const facet = url.searchParams.get('facet')\n if (!facet) {\n sendJson(res, 400, { error: 'missing facet' })\n return\n }\n sendJson(res, 200, store.facetDistribution(facet))\n return\n }\n if (path === '/api/measures') {\n sendJson(res, 200, store.measureList())\n return\n }\n if (path === '/api/breakdown') {\n const q = url.searchParams\n const measure = q.get('measure')\n if (!measure) {\n sendJson(res, 400, { error: 'missing measure' })\n return\n }\n const reserved = new Set(['measure', 'by', 'from', 'to', 'tool_name'])\n const filters: Record<string, string> = {}\n for (const [k, v] of q.entries()) {\n if (!reserved.has(k) && v) filters[k] = v\n }\n const window = { from: q.get('from') ?? undefined, to: q.get('to') ?? undefined }\n const toolNames = q.getAll('tool_name').filter(Boolean)\n sendJson(res, 200, store.breakdown(measure, q.get('by') ?? undefined, filters, window, toolNames))\n return\n }\n if (path === '/api/kpis') {\n // Headline KPI row, windowed (default 7 days) plus the same-length prior\n // period so the UI can show a delta. Session-grain metrics window by session\n // start; cost-per-artifact by completion (see Store.costPerArtifact). No\n // ticket source in this CLI (would need a Jira/Linear adapter), so the\n // cost-per-artifact KPIs are PR + feature only.\n // Outcome types counting as success for the rate KPI (the UI's editable\n // success definition). Empty → Store.kpis defaults to ['session_success'].\n const outcomes = (url.searchParams.get('outcomes') ?? '')\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n // Window = the dashboard's top-level selector. 'all' → whole history with no\n // prior period (so no deltas); a number → that many days plus the same-length\n // prior period.\n const daysRaw = url.searchParams.get('days') ?? '7'\n if (daysRaw === 'all') {\n sendJson(res, 200, { days: 'all', window: null, current: store.kpis(undefined, undefined, outcomes), previous: null })\n return\n }\n const parsed = parseInt(daysRaw, 10)\n const days = Number.isFinite(parsed) && parsed > 0 ? parsed : 7\n const span = days * 86_400_000\n const now = Date.now()\n const to = new Date(now).toISOString()\n const from = new Date(now - span).toISOString()\n const prevFrom = new Date(now - 2 * span).toISOString()\n sendJson(res, 200, {\n days,\n window: { from, to },\n current: store.kpis(from, to, outcomes),\n previous: store.kpis(prevFrom, from, outcomes),\n })\n return\n }\n if (path === '/api/ops-over-time') {\n // Operational tool-call metrics over time. view = tool_calls | error_rate |\n // skill_usage; by=name|error_category splits the series; bucket day|week|month.\n // tool_name / error_category are repeatable ROW-level scopes for the error-rate\n // chart (which calls count; which errors count); any other param is a generic\n // session-level facet filter.\n const q = url.searchParams\n const rawView = q.get('view')\n const view: 'tool_calls' | 'error_rate' | 'skill_usage' =\n rawView === 'tool_calls' || rawView === 'skill_usage' ? rawView : 'error_rate'\n const rawBucket = q.get('bucket')\n const bucket: Bucket = rawBucket === 'day' || rawBucket === 'month' ? rawBucket : 'week'\n const rawBy = q.get('by')\n const by = rawBy === 'name' || rawBy === 'error_category' ? rawBy : undefined\n const reserved = new Set(['view', 'bucket', 'by', 'from', 'to', 'topK', 'tool_name', 'error_category'])\n const filters: Record<string, string[]> = {}\n for (const [k, v] of q.entries()) {\n if (!reserved.has(k) && v) (filters[k] ??= []).push(v)\n }\n const opsTopK = parseInt(q.get('topK') ?? '', 10)\n sendJson(\n res,\n 200,\n store.opsOverTime({\n view,\n bucket,\n by,\n from: q.get('from') ?? undefined,\n to: q.get('to') ?? undefined,\n filters,\n toolNames: q.getAll('tool_name').filter(Boolean),\n errorCategories: q.getAll('error_category').filter(Boolean),\n topK: Number.isFinite(opsTopK) && opsTopK > 0 ? opsTopK : undefined,\n }),\n )\n return\n }\n if (path === '/api/tool-names') {\n // Distinct tool names (busiest first) for the Ops error-rate tool filter.\n sendJson(res, 200, store.toolNames())\n return\n }\n if (path === '/api/sessions-over-time') {\n // Session count per bucket, optionally split into one series per facet value.\n const q = url.searchParams\n const rawBucket = q.get('bucket')\n const bucket: Bucket = rawBucket === 'day' || rawBucket === 'month' ? rawBucket : 'week'\n const reserved = new Set(['bucket', 'by', 'from', 'to', 'topK'])\n // Repeated params (?model=a&model=b) collect into one OR'd filter per facet.\n const filters: Record<string, string[]> = {}\n for (const [k, v] of q.entries()) {\n if (!reserved.has(k) && v) (filters[k] ??= []).push(v)\n }\n const sessTopK = parseInt(q.get('topK') ?? '', 10)\n sendJson(\n res,\n 200,\n store.sessionsOverTime({\n bucket,\n by: q.get('by') || undefined,\n from: q.get('from') ?? undefined,\n to: q.get('to') ?? undefined,\n filters,\n topK: Number.isFinite(sessTopK) && sessTopK > 0 ? sessTopK : undefined,\n }),\n )\n return\n }\n if (path === '/api/spend-over-time') {\n // The burn / total-spend-breakdown view: spend per bucket, optionally split\n // into one series per facet value. `bucket` day|week|month; `by` optional\n // facet; any other query param is a session-level filter.\n const q = url.searchParams\n const rawBucket = q.get('bucket')\n const bucket: Bucket = rawBucket === 'day' || rawBucket === 'month' ? rawBucket : 'week'\n const reserved = new Set(['bucket', 'by', 'from', 'to', 'topK'])\n // Repeated params (?model=a&model=b) collect into one OR'd filter per facet.\n const filters: Record<string, string[]> = {}\n for (const [k, v] of q.entries()) {\n if (!reserved.has(k) && v) (filters[k] ??= []).push(v)\n }\n const spendTopK = parseInt(q.get('topK') ?? '', 10)\n sendJson(\n res,\n 200,\n store.spendOverTime({\n bucket,\n by: q.get('by') || undefined,\n from: q.get('from') ?? undefined,\n to: q.get('to') ?? undefined,\n filters,\n topK: Number.isFinite(spendTopK) && spendTopK > 0 ? spendTopK : undefined,\n }),\n )\n return\n }\n if (path === '/api/cost-artifact') {\n // Cost-per-shipped-artifact detail: the windowed unit-cost KPI (current +\n // prior period for a delta) plus the two decomposition curves (burn,\n // throughput) over the same window and the burn-efficiency period sum.\n // `days` is a number or 'all' (whole history, no prior period); `kind` is\n // feature | pr.\n const q = url.searchParams\n const kind = q.get('kind') === 'pr' ? 'pr' : 'feature'\n const rawBucket = q.get('bucket')\n const bucket: Bucket = rawBucket === 'day' || rawBucket === 'month' ? rawBucket : 'week'\n const complexity = q.get('complexity') || undefined\n const daysRaw = q.get('days') ?? '7'\n if (daysRaw === 'all') {\n const curves = store.costCurves(kind, bucket, undefined, undefined, complexity)\n sendJson(res, 200, {\n kind,\n days: 'all',\n complexity: complexity || null,\n window: null,\n kpi: { current: store.costPerArtifact(kind, undefined, undefined, complexity), previous: null },\n burn: curves.burn,\n throughput: curves.throughput,\n reviewed: curves.reviewed,\n buckets: curves.buckets,\n })\n return\n }\n const parsed = parseInt(daysRaw, 10)\n const days = Number.isFinite(parsed) && parsed > 0 ? parsed : 7\n const span = days * 86_400_000\n const now = Date.now()\n const to = new Date(now).toISOString()\n const from = new Date(now - span).toISOString()\n const prevFrom = new Date(now - 2 * span).toISOString()\n // Curves share the KPI's window so the chart shows the same N days.\n const curves = store.costCurves(kind, bucket, from, to, complexity)\n sendJson(res, 200, {\n kind,\n days,\n complexity: complexity || null,\n window: { from, to },\n kpi: { current: store.costPerArtifact(kind, from, to, complexity), previous: store.costPerArtifact(kind, prevFrom, from, complexity) },\n burn: curves.burn,\n throughput: curves.throughput,\n reviewed: curves.reviewed,\n buckets: curves.buckets,\n })\n return\n }\n if (path === '/api/feature-costs') {\n // Hierarchical cost-per-feature for the cost-artifact section's breakdown\n // charts (icicle / treemap): every feature with its own (direct) cost and the\n // subtree rollup over parent_artifact_id. `days` scopes to features SHIPPED in\n // the window (per-feature cost stays all-time); `complexity` scopes by bucket.\n const { from, to } = windowFromDays(url.searchParams.get('days'))\n sendJson(res, 200, { nodes: store.featureCostTree(url.searchParams.get('complexity') || undefined, from, to) })\n return\n }\n if (path === '/api/outcome-types') {\n sendJson(res, 200, store.outcomeTypes())\n return\n }\n if (path === '/api/success-rate') {\n // Session Outcome Rate over time. `outcomes` = the success set (numerator,\n // default session_success); `bucket` = day|week|month; `by` = optional facet\n // to split into series; any other query param is a session-level filter.\n const q = url.searchParams\n const outcomes = (q.get('outcomes') ?? '')\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n const rawBucket = q.get('bucket')\n const bucket: Bucket = rawBucket === 'day' || rawBucket === 'month' ? rawBucket : 'week'\n const reserved = new Set(['outcomes', 'bucket', 'by', 'from', 'to', 'topK'])\n // Repeated params (?model=a&model=b) collect into one OR'd filter per facet.\n const filters: Record<string, string[]> = {}\n for (const [k, v] of q.entries()) {\n if (!reserved.has(k) && v) (filters[k] ??= []).push(v)\n }\n const srTopK = parseInt(q.get('topK') ?? '', 10)\n sendJson(\n res,\n 200,\n store.successRate({\n outcomes: outcomes.length ? outcomes : ['session_success'],\n bucket,\n by: q.get('by') || undefined,\n from: q.get('from') ?? undefined,\n to: q.get('to') ?? undefined,\n filters,\n topK: Number.isFinite(srTopK) && srTopK > 0 ? srTopK : undefined,\n }),\n )\n return\n }\n if (path === '/api/artifact-suggest') {\n // Typeahead for the session-list artifact search.\n const q = url.searchParams.get('q') ?? ''\n const kind = url.searchParams.get('kind') || undefined\n const lim = parseInt(url.searchParams.get('limit') ?? '', 10)\n const limit = Number.isFinite(lim) && lim > 0 ? Math.min(lim, 25) : 10\n sendJson(res, 200, store.suggestArtifacts(q, kind, limit))\n return\n }\n if (path === '/api/session-links/suggest') {\n const sessionId = url.searchParams.get('sessionId')\n const q = url.searchParams.get('q') ?? ''\n const kind = url.searchParams.get('kind') || undefined\n if (!sessionId) {\n sendJson(res, 400, { error: 'sessionId required' })\n return\n }\n sendJson(res, 200, store.suggestLinkableArtifacts(sessionId, q, kind))\n return\n }\n if (path === '/api/artifacts') {\n // Optional `complexity` scopes to artifacts of the selected bucket(s) (applies\n // only when a single `kind` is given — the bucket→value mapping is per-kind).\n // Optional `days` scopes to artifacts completed in the window (all-time when\n // absent — the Artifacts tab relies on that to list still-open PRs). `shipped=1`\n // (the cost treemap) keeps only completed/merged, produced (non-review) artifacts.\n const { from, to } = windowFromDays(url.searchParams.get('days'))\n sendJson(\n res,\n 200,\n store.artifactList(\n url.searchParams.get('kind') ?? undefined,\n url.searchParams.get('complexity') || undefined,\n from,\n to,\n url.searchParams.get('shipped') === '1',\n ),\n )\n return\n }\n if (path === '/api/timeseries') {\n const raw = url.searchParams.get('bucket')\n const bucket: Bucket = raw === 'day' || raw === 'month' ? raw : 'week'\n sendJson(\n res,\n 200,\n store.timeseries(bucket, url.searchParams.get('from') ?? undefined, url.searchParams.get('to') ?? undefined),\n )\n return\n }\n if (path === '/api/sessions') {\n const q = url.searchParams\n const limit = q.get('limit')\n // Any non-reserved query param is treated as a facet filter; sessionList\n // validates keys against the registry and ignores unknown ones.\n const reserved = new Set(['q', 'artifact', 'artifact_kind', 'from', 'to', 'outcome_types', 'limit'])\n const facets: Record<string, string> = {}\n for (const [k, v] of q.entries()) {\n if (!reserved.has(k) && v) facets[k] = v\n }\n const outcomeTypesRaw = q.get('outcome_types')\n sendJson(\n res,\n 200,\n store.sessionList({\n facets,\n q: q.get('q') ?? undefined,\n artifact: q.get('artifact') ?? undefined,\n artifactKind: q.get('artifact_kind') ?? undefined,\n from: q.get('from') ?? undefined,\n to: q.get('to') ?? undefined,\n outcomeTypes: outcomeTypesRaw ? outcomeTypesRaw.split(',').filter(Boolean) : undefined,\n limit: limit ? parseInt(limit, 10) : undefined,\n }),\n )\n return\n }\n if (path === '/api/session') {\n const id = url.searchParams.get('id')\n if (!id) {\n sendJson(res, 400, { error: 'missing id' })\n return\n }\n const detail = store.sessionDetail(id)\n if (!detail) {\n sendJson(res, 404, { error: 'not found' })\n return\n }\n sendJson(res, 200, detail)\n return\n }\n if (path === '/api/session-files') {\n const id = url.searchParams.get('id')\n if (!id) {\n sendJson(res, 400, { error: 'missing id' })\n return\n }\n sendJson(res, 200, { edits: store.fileChanges(id) })\n return\n }\n sendJson(res, 404, { error: 'not found' })\n}\n\nfunction readBody(req: IncomingMessage): Promise<Record<string, any>> {\n return new Promise((resolve) => {\n let data = ''\n req.on('data', (chunk) => {\n data += chunk\n if (data.length > 1_000_000) data = data.slice(0, 1_000_000) // guard\n })\n req.on('end', () => {\n try {\n resolve(data ? JSON.parse(data) : {})\n } catch {\n resolve({})\n }\n })\n req.on('error', () => resolve({}))\n })\n}\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n const json = JSON.stringify(body)\n res.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })\n res.end(json)\n}\n\n// A dashboard time window from the shared `days` param (a positive number of days,\n// or 'all'/missing for all-time). Ending \"now\", matching the cost-artifact/KPI\n// window derivation so every panel scopes to the same span.\nfunction windowFromDays(daysRaw: string | null): { from?: string; to?: string } {\n if (!daysRaw || daysRaw === 'all') return {}\n const parsed = parseInt(daysRaw, 10)\n const days = Number.isFinite(parsed) && parsed > 0 ? parsed : 7\n const now = Date.now()\n return { from: new Date(now - days * 86_400_000).toISOString(), to: new Date(now).toISOString() }\n}\n\n// The dashboard SPA is built by tsup into dist/client (app.js + index.html +\n// styles.css) and served from there. Resolve that directory once, tolerating\n// both prod (this module runs from dist/) and dev (tsx from src/, where the\n// build still emits to <pkg>/dist/client).\nlet CLIENT_DIR: string | null = null\nfunction clientDir(): string {\n if (CLIENT_DIR) return CLIENT_DIR\n let dir = dirname(fileURLToPath(import.meta.url))\n for (let i = 0; i < 6; i++) {\n if (existsSync(join(dir, 'client', 'index.html'))) return (CLIENT_DIR = join(dir, 'client'))\n if (existsSync(join(dir, 'dist', 'client', 'index.html'))) return (CLIENT_DIR = join(dir, 'dist', 'client'))\n dir = dirname(dir)\n }\n return (CLIENT_DIR = join(process.cwd(), 'dist', 'client'))\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n}\n\nasync function serveClientAsset(res: ServerResponse, urlPath: string): Promise<void> {\n const base = clientDir()\n const file = join(base, urlPath.slice('/client/'.length))\n if (!file.startsWith(base)) {\n sendJson(res, 403, { error: 'forbidden' })\n return\n }\n const ext = file.slice(file.lastIndexOf('.'))\n await sendFile(res, file, CONTENT_TYPES[ext] ?? 'application/octet-stream')\n}\n\nasync function sendFile(res: ServerResponse, file: string, type: string): Promise<void> {\n try {\n const buf = await readFile(file)\n res.writeHead(200, { 'content-type': type })\n res.end(buf)\n } catch {\n sendJson(res, 404, { error: 'not found' })\n }\n}\n","import { execFile } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { createInterface } from 'node:readline'\nimport { loadConfig } from '../config'\nimport { createDashboardServer, type ShFn } from '../server/http'\nimport { openDb } from '../store/db'\nimport { Store } from '../store/store'\nimport { createLogger } from '../util/log'\n\nfunction makeSh(): ShFn {\n return (cmd, args) =>\n new Promise((resolve) => {\n execFile(cmd, args, { timeout: 10_000 }, (err, stdout, stderr) => {\n if (err && (err as NodeJS.ErrnoException).code === 'ENOENT') resolve(null)\n else resolve({ code: err ? (err as any).status ?? 1 : 0, stdout: (stdout || stderr) ?? '' })\n })\n })\n}\n\nexport interface ServeOptions {\n db?: string\n port?: number\n open?: boolean\n}\n\n/** Serve the dashboard over an already-analyzed store. Reads only; Ctrl+C stops. */\nexport async function serve(opts: ServeOptions): Promise<void> {\n const log = createLogger('info')\n const config = loadConfig({ db: opts.db })\n if (!existsSync(config.dbPath)) {\n log.error(`no store at ${config.dbPath} — run \\`tuneloop analyze\\` first`)\n process.exitCode = 1\n return\n }\n\n const db = openDb(config.dbPath)\n const store = new Store(db)\n const port = opts.port ?? 4319\n const url = `http://localhost:${port}`\n const server = createDashboardServer(store, config.dbPath, makeSh())\n\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') log.error(`port ${port} is in use — try --port <n>`)\n else log.error(err.message)\n process.exitCode = 1\n })\n\n // Wait for the user to hit Enter before opening a browser tab rather than\n // launching one unprompted — an auto-opened tab is disruptive when the developer\n // is mid-task in their browser. Needs an interactive TTY to read the keypress;\n // --no-open (or a non-TTY stdin) serves headless with no prompt.\n const interactive = opts.open !== false && Boolean(process.stdin.isTTY)\n\n // Bind to loopback only. The dashboard serves session transcripts (which can\n // contain proprietary code and secrets) over an unauthenticated API; without an\n // explicit host Node binds 0.0.0.0, exposing it to the whole LAN. tuneloop is a\n // local single-developer tool, so 127.0.0.1 is the correct surface.\n server.listen(port, '127.0.0.1', () => {\n const hint = interactive ? 'Enter to open in your browser · Ctrl+C to stop' : 'Ctrl+C to stop'\n process.stdout.write(`\\n tuneloop dashboard ${url}\\n store: ${config.dbPath}\\n ${hint}\\n\\n`)\n })\n\n await new Promise<void>((resolve) => {\n // terminal:false so readline doesn't intercept Ctrl+C; the shell's cooked-mode\n // stdin still delivers SIGINT to the process and a newline as a 'line' event.\n const rl = interactive ? createInterface({ input: process.stdin, terminal: false }) : undefined\n rl?.on('line', () => tryOpen(url))\n process.on('SIGINT', () => {\n rl?.close()\n server.close()\n store.close()\n process.stdout.write('\\nstopped.\\n')\n resolve()\n })\n })\n}\n\nfunction tryOpen(url: string): void {\n const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open'\n const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url]\n execFile(cmd, args, () => {\n /* best effort — fine if no browser opener exists */\n })\n}\n","/**\n * The read-only SQL escape hatch: run arbitrary SELECTs over the local store for\n * the ~10 analyses the curated dashboard doesn't cover (sidechain cost split,\n * tool latency via duration_ms, token-type economics, PR cycle time, provider /\n * branch slicing, generic tool `action` breakdowns, …).\n *\n * Safety is layered — every query must clear all of these before a row is read:\n * 1. a SEPARATE connection opened `readonly` + `query_only` (never the Store's\n * writable handle), so a write can't reach the on-disk DB even in principle;\n * 2. single-statement compilation — better-sqlite3's `prepare` throws on stacked\n * SQL, killing `SELECT …; DROP …;` style injection;\n * 3. a leading-keyword allowlist (SELECT / WITH) — a readonly PRAGMA is still a\n * \"reader\", so the reader gate alone would let `PRAGMA`/`ATTACH` through;\n * 4. the `stmt.reader === true` gate — a clear error instead of a cryptic one\n * for anything non-row-returning that slips past (3);\n * 5. `session_blobs` is hard-excluded — it holds gzipped raw transcripts that\n * can contain proprietary code and secrets; the fact tables are the surface;\n * 6. row / byte / wall-clock caps enforced while iterating, so a runaway query\n * is bounded. (better-sqlite3 has no statement interrupt, so the time cap can\n * only fire BETWEEN produced rows — a query slow to yield its first row still\n * blocks. Acceptable for a local single-dev tool.)\n */\nimport Database from 'better-sqlite3'\nimport { INTRINSIC_FACETS, type FacetSpec } from '../core/facets'\nimport { INTRINSIC_MEASURES, type MeasureSpec } from '../core/measures'\nimport { openDb, type DB } from '../store/db'\n\nexport const DEFAULT_MAX_ROWS = 1000\nexport const DEFAULT_MAX_BYTES = 5_000_000\nexport const DEFAULT_TIMEOUT_MS = 5_000\n\n/**\n * Tables never reachable via ad-hoc query. `session_blobs` is the gzipped raw\n * transcript store — excluding it is the whole point of \"query the facts, not the\n * transcripts\". Kept as a list so a future curated-VIEW policy can extend it.\n */\nexport const FORBIDDEN_TABLES = ['session_blobs']\n\nexport interface QueryOptions {\n /** Stop after this many rows (default 1000). */\n maxRows?: number\n /** Stop once accumulated JSON size exceeds this (default 5MB). */\n maxBytes?: number\n /** Stop if row production exceeds this wall-clock budget (default 5s). */\n timeoutMs?: number\n /** Positional (?) or named (:name) bind parameters. */\n params?: unknown[] | Record<string, unknown>\n}\n\nexport interface QueryResult {\n columns: string[]\n rows: Record<string, unknown>[]\n rowCount: number\n /** Which cap ended the read early, or null if the full result fit. */\n truncated: 'rows' | 'bytes' | 'time' | null\n elapsedMs: number\n}\n\n/** Rejected before touching the DB: shape violations the SQL engine wouldn't flag. */\nexport class QueryError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'QueryError'\n }\n}\n\n/** Strip leading line/block comments so the first real keyword can be checked. */\nfunction stripLeadingComments(sql: string): string {\n let s = sql\n for (;;) {\n const t = s.trimStart()\n if (t.startsWith('--')) {\n const nl = t.indexOf('\\n')\n s = nl === -1 ? '' : t.slice(nl + 1)\n } else if (t.startsWith('/*')) {\n const end = t.indexOf('*/')\n s = end === -1 ? '' : t.slice(end + 2)\n } else {\n return t\n }\n }\n}\n\n/** Enforce the static (pre-execution) guards: SELECT/WITH only, no forbidden tables. */\nexport function assertReadOnlyShape(sql: string): void {\n const head = stripLeadingComments(sql)\n if (!/^(select|with)\\b/i.test(head)) {\n throw new QueryError('only read-only SELECT (or WITH … SELECT) queries are allowed')\n }\n for (const table of FORBIDDEN_TABLES) {\n if (new RegExp(`\\\\b${table}\\\\b`, 'i').test(sql)) {\n throw new QueryError(\n `${table} is excluded from tuneloop query — it holds raw session transcripts (code + secrets). Query the fact tables instead.`,\n )\n }\n }\n}\n\n/** Rough byte size of a row for the response cap; exact enough to bound memory. */\nfunction approxBytes(row: unknown): number {\n try {\n return JSON.stringify(row)?.length ?? 0\n } catch {\n return 0\n }\n}\n\n/**\n * Run a single read-only SELECT against the store at `dbPath`. Opens and closes\n * its own connection every call — cheap, and keeps this fully independent of any\n * live Store/serve handle. Throws {@link QueryError} for guard violations and\n * SQLite's own errors (syntax, unknown column) for genuine SQL mistakes.\n */\nexport function runQuery(dbPath: string, sql: string, opts: QueryOptions = {}): QueryResult {\n assertReadOnlyShape(sql)\n const maxRows = opts.maxRows && opts.maxRows > 0 ? opts.maxRows : DEFAULT_MAX_ROWS\n const maxBytes = opts.maxBytes && opts.maxBytes > 0 ? opts.maxBytes : DEFAULT_MAX_BYTES\n const timeoutMs = opts.timeoutMs && opts.timeoutMs > 0 ? opts.timeoutMs : DEFAULT_TIMEOUT_MS\n const params = opts.params ?? []\n\n const db = new Database(dbPath, { readonly: true, fileMustExist: true })\n try {\n db.pragma('query_only = true')\n\n let stmt: Database.Statement\n try {\n stmt = db.prepare(sql) // throws on stacked statements — single-statement only\n } catch (err) {\n throw new QueryError((err as Error).message)\n }\n if (!stmt.reader) {\n throw new QueryError('only read-only SELECT queries are allowed')\n }\n\n const columns = stmt.columns().map((c) => c.name)\n const rows: Record<string, unknown>[] = []\n let bytes = 0\n let truncated: QueryResult['truncated'] = null\n const started = Date.now()\n\n const iter = stmt.iterate(...(Array.isArray(params) ? params : [params]))\n for (const row of iter as IterableIterator<Record<string, unknown>>) {\n if (rows.length >= maxRows) {\n truncated = 'rows'\n break\n }\n if (Date.now() - started > timeoutMs) {\n truncated = 'time'\n break\n }\n bytes += approxBytes(row)\n if (bytes > maxBytes) {\n truncated = 'bytes'\n break\n }\n rows.push(row)\n }\n // Breaking a for-of calls iter.return(), which better-sqlite3 uses to reset the\n // statement, so the connection is clean to close below.\n\n return { columns, rows, rowCount: rows.length, truncated, elapsedMs: Date.now() - started }\n } finally {\n db.close()\n }\n}\n\nexport interface SchemaTable {\n name: string\n /** The CREATE statement as SQLite normalized it — guaranteed in sync with the store. */\n sql: string\n}\n\n/** What's actually in the store — the extent, not the shape. Derived from `sessions`. */\nexport interface Coverage {\n sessions: number\n firstAt: string | null\n lastAt: string | null\n lastAnalyzedAt: string | null\n sources: { source: string | null; count: number }[]\n repos: number\n cwds: number\n /** Source directories scanned, with each one's last-analyzed time (empty on pre-v9 stores). */\n roots: { source: string | null; path: string; lastAnalyzedAt: string | null }[]\n}\n\nexport interface SchemaDump {\n schemaVersion: number | null\n /** Store extent; null when reflecting the canonical (empty) schema. */\n coverage: Coverage | null\n tables: SchemaTable[]\n facets: FacetSpec[]\n measures: MeasureSpec[]\n}\n\n/** Aggregate the store's extent from `sessions` + the `last_analyze_at` meta row. */\nexport function coverageFromDb(db: DB): Coverage {\n const agg = db\n .prepare(\n `SELECT COUNT(*) AS sessions, MIN(started_at) AS firstAt, MAX(started_at) AS lastAt,\n COUNT(DISTINCT repo) AS repos, COUNT(DISTINCT cwd) AS cwds\n FROM sessions`,\n )\n .get() as { sessions: number; firstAt: string | null; lastAt: string | null; repos: number; cwds: number }\n const sources = db\n .prepare(`SELECT source, COUNT(*) AS count FROM sessions GROUP BY source ORDER BY count DESC`)\n .all() as { source: string | null; count: number }[]\n const lastAnalyzedAt =\n (db.prepare(`SELECT value FROM meta WHERE key = 'last_analyze_at'`).get() as { value?: string } | undefined)?.value ?? null\n return { ...agg, sources, lastAnalyzedAt, roots: analyzedRoots(db) }\n}\n\n/** Read the ingest-provenance table; [] on pre-v9 stores that predate it. */\nfunction analyzedRoots(db: DB): Coverage['roots'] {\n const exists = db\n .prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'analyzed_roots'`)\n .get()\n if (!exists) return []\n return db\n .prepare(`SELECT path, source, last_analyzed_at AS lastAnalyzedAt FROM analyzed_roots ORDER BY source, path`)\n .all() as Coverage['roots']\n}\n\n/**\n * Reflect the store's schema straight from `sqlite_master` (plus the intrinsic\n * facet/measure registries). Reading the live DDL means this can't drift from the\n * SCHEMA that actually built the store. `session_blobs` is omitted to match the\n * query surface. Shared by the CLI `--schema` dump and the skill generator (which\n * passes an in-memory store, so the checked-in skill doc stays in sync too).\n */\nexport function schemaFromDb(db: DB): SchemaDump {\n const tables = db\n .prepare(\n `SELECT name, sql FROM sqlite_master\n WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'\n ORDER BY name`,\n )\n .all() as SchemaTable[]\n const versionRow = db.prepare(`SELECT value FROM meta WHERE key = 'schema_version'`).get() as\n | { value?: string }\n | undefined\n return {\n schemaVersion: versionRow?.value ? Number(versionRow.value) : null,\n coverage: null, // shape only; describeSchema() fills coverage for a live store\n tables: tables.filter((t) => !FORBIDDEN_TABLES.includes(t.name)),\n facets: INTRINSIC_FACETS,\n measures: INTRINSIC_MEASURES,\n }\n}\n\n/** Open the store read-only and dump its schema (see {@link schemaFromDb}). */\nexport function describeSchema(dbPath: string): SchemaDump {\n const db = new Database(dbPath, { readonly: true, fileMustExist: true })\n try {\n db.pragma('query_only = true')\n const handle = db as unknown as DB\n return { ...schemaFromDb(handle), coverage: coverageFromDb(handle) }\n } finally {\n db.close()\n }\n}\n\n/** Build a fresh in-memory store purely to reflect the canonical schema (no data). */\nexport function canonicalSchema(): SchemaDump {\n const db = openDb(':memory:')\n try {\n return schemaFromDb(db)\n } finally {\n db.close()\n }\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["join","join","readFile","readFile","ev","content","join","homedir","join","readFile","mapAction","PARSE_VERSION","SOURCE","PROVIDER","readFile","num","mapAction","PARSE_VERSION","join","homedir","homedir","join","join","FILE_WRITE","FILE_READ","SEARCH","WEB","SHELL","mapAction","filePaths","PARSE_VERSION","SOURCE","usageOf","mapAction","num","SOURCE","PARSE_VERSION","join","homedir","clip","num","basename","basename","num","title","homedir","join","resolve","resolve","join","homedir","usageOf","textOf","usageOf","num","bucket","table","sql","r","rows","clip","ok","existsSync","basename","resolve","dirname","Database","table","processors","adapters","resolve","existsSync","basename","readFile","existsSync","dirname","join","ok","join","curves","resolve","dirname","existsSync","readFile","execFile","existsSync","makeSh","resolve","execFile","existsSync","Database","table","Database"]}